文章目录
- Spring
- IOC控制反转(存)
- DI依赖注入(取)
- Spring的优点
- Spring的核心技术
- 入门案例
- 入门案例1
- 入门案例2
- 注解
- 配置类
- 组件注册功能(IOC)
- 类直接注册
- 配置类中注册(JavaConfig)
- 组件注入功能(DI)
- 配置类注解中的`@Bean`
- 构造器注入
- 方法注入
- 成员变量注入
- Spring单元测试
- Bean
- Bean的实例化
- 作用域Scope
- 生命周期
- 初始化阶段的方法
- 容器关闭阶段的方法
Spring
- Spring Framework通常人们称之为Spring
- 框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架
- Spring是一个分层的Java SE/EE full-stack(一站式) 轻量级开源框架
eg:
上图中AdminServiceImpl类型的实例其实创建了多次,去建立引用关系多次,维护起来比较麻烦,也浪费了内存的空间
想要做的事情:在整个应用程序中使用同一个adminServiceImpl的实例
- 将其放在ConcurrentHashMap中,然后后面每一次使用的时候通过同一个ConcurrentHashMap来取
- 要保证使用的是同一个ConcurrentHashMap,使用ServletContext保存,并且做到了整个应用程序中的共享
@WebListener
public class InitializationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
Map<String, Object> data = new ConcurrentHashMap<>();
data.put("adminService", new AdminServiceImpl());
servletContext.setAttribute("applicationData", data);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
使用:
ServletContext servletContext = getServletContext();
Map map = (Map) servletContext.getAttribute("applicationData");
AdminService adminService = (AdminService) map.get("adminService");
IOC控制反转(存)
Inverse of Control
- 控制:实例的生成权
- 应用程序控制实例的生成
- 反转:原来是应用程序控制实例的生成,现在把这个过程反转给Spring
- Spring框架来控制实例的生成
- 容器:Spring容器、IOC容器、容器
- 管理的实例
- 组件:Spring管理的实例对象可以称为组件
- 注册:提供一些信息给Spring容器,管理这些信息对应的实例
- 这些实例就成为了Spring容器的组件
- 控制:实例的生成权
- 获取依赖对象被反转了,它是被动获取;
- 由Spring去生成实例,并由Spring去管理实例
- 正转就是自己去new一个对象,自己获取对象
DI依赖注入(取)
- 经过了控制反转,Spring容器管理应用程序运行过程中的实例,从Spring容器中把这些实例取出来,就是依赖注入。
- 核心点:从容器中获得应用程序所需要的实例,并且给应用程序中的成员变量做赋值
- DI这个概念是IOC的延续,DI是在IOC的基础上
Spring的优点
- 方便解耦,简化开发(高内聚低耦合)
- AOP编程的支持
- 声明式 事务的支持
- 方便程序的测试
- 方便集成各种优秀框架
- 降低JavaEE API的使用难度
Spring的核心技术
依赖注入、事件、资源、i18(nternationalizatio)n国际化、校验、数据绑定、类型转换、Spring Expression Language、AOP(面向切面编程)
入门案例
入门案例1
- 导入依赖
<dependencies>
<!--引入依赖 beans、context、aop、expression、core、jcl 5+1-->
<!--spring-context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
- 提供接口和实现类
public interface UserService {
public void sayHello(String name);
}
public class UserServiceImpl implements UserService{
@Override
public void sayHello(String name) {
System.out.println("hello " + name);
}
}
- 原先使用实例的时候,是通过构造方法生成的;后续要变更为来源Spring容器
- 在配置文件中注册组件
<!--spring配置文件的名称通常叫application(-xxx).xml-->
<!-- bean definitions here -->
<!--控制反转-->
<!--id属性 -- 组件在容器中的唯一标识-->
<!--name属性 -- 名称 -- 通常省略不写-->
<!--class 全类名 -- 实现类的全类名-->
<!--组件 注册 -- 将实例交给spring管理的过程我们称之为注册-->
<bean id="userService" class="com.coo1heisenberg.demo1.service.UserServiceImpl"/>
- 从容器中取出组件,执行方法
- ApplicationContext底层封装了ConcurrentHashMap
- ClassPathXmlApplicationContext加载的是
.xml
配置文件 - AnnotationConfigApplicationContext加载的是配置类
- ClassPathXmlApplicationContext加载的是
- ApplicationContext底层封装了ConcurrentHashMap
- 方法1:按照组件的id/name取出组件
// spring容器
// 1. 初始化容器,并注册组件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
// 2. 从容器中取出组件
UserService userService1 = (UserService) applicationContext.getBean("userService");
userService1.sayHello("spring");
// hello spring
- 方法2:可以写接口的class,也可以写实现类的class
// 可以写接口的class,也可以写实现类的class
// 建议写接口
// 如果容器中某个类型的组件只有一个,可以按照类型取出
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
UserService userService2 = applicationContext.getBean(UserService.class);
- 方法3:id + 类型
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
UserService userService3 = applicationContext.getBean("userService", UserService.class);
入门案例2
- 维护组件之间的依赖关系,在容器中注册dao层和service层组件,并且service层的组件依赖于dao层组件
- dao类
public interface UserDao {
}
public class UserDaoImpl implements UserDao{
}
- service类
public interface UserService {
public void sayHello(String name);
}
public class UserServiceImpl implements UserService{
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void sayHello(String name) {
System.out.println("hello " + name);
}
}
- application.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.coo1heisenberg.demo1.service.UserServiceImpl">
<!--注册userServiceImpl这个类型的组件的时候,实例化的过程会执行set方法(setUserDao)
这个set方法的形参是UserDao类型的实例,通过id从容器中取出作为形参
this.userDao = userDao; 给userServiceImpl这个组件的userDao成员变量进行赋值
-> 依赖注入
-->
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.coo1heisenberg.demo1.dao.UserDaoImpl"/>
</beans>
- 单元测试类
@Test
public void test2() {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("application.xml");
UserDao userDao = applicationContext.getBean(UserDao.class);
// 直接从容器中取出的userDao实例和直接从容器中取出的userService实例中的userDao成员变量是否是同一个
UserService userService = applicationContext.getBean(UserService.class);
// userDao的地址值与上面的同一个
}
注解
配置类
- 配置类:承担做通用配置的功能,同时在配置类中可以组件注册
- 我们在类定义上增加一些功能性的注解,增加一些通用性的配置
- 我们在类中的方法里注册组件
eg:
/**
* 配置类;替代配置文件
*
* 提供配置类,后面初始化容器的时候,加载的就是配置类(不再是配置文件)
*/
// 这里增加功能性的注解
@Configuration
public class ApplicationConfiguration {
// 类中的方法做组件的注册
}
组件注册功能(IOC)
类直接注册
@Component
组件
@Target(ElementType.TYPE)// 该注解写在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
过程:
- 组件注册功能首先要打开扫描开关
@Configuration
@ComponentScan("com.coo1heisenberg.demo2")
public class ApplicationConfiguration {
}
- 组件注册功能的注解
@Component
@Component
public class AdminServiceImpl implements AdminService{
}
思考:为什么我们提供一个扫描包目录(@ComponentScan("包目录")
),然后在包目录下的类中使用注解就可以注册组件?
- 提供包目录,能否获得这个包以及这个包的子包下的所有的类的全限定类名
- 通过全限定类名,通过反射的方式获得对应的class →
Class.forName()
List<Class> classList =
通过上面的过程获得- 遍历获得其中的单个class呢
class.isAnnotationPresent(注解的class)
→ 判断这个类上是否有注解
eg:
@SneakyThrows
@Test
public void testIsAnnotationPresent() {
Map<String,Object> map = new ConcurrentHashMap<>();
List<Class<?>> classList = Arrays.asList(UserServiceImpl.class,
GoodServiceImpl.class,
UserServiceImpl.class);
for (Class<?> singleClass : classList) {
if (singleClass.isAnnotationPresent(Component.class)) {
Object instance = singleClass.newInstance();
String key = singleClass.getName();
map.put(key, instance);
}
}
System.out.println(map);
}
- 组件id值的设定
/**
* 增加其value属性,value属性值就是id
* 如果没有增加value属性,id默认值就是类名的首字母小写
*/
@Component // 组件id的默认值是adminServiceImpl
@Component("adminService") // 使用value属性值指定了组件id为adminService
public class AdminServiceImpl implements AdminService{
}
- 类似的注解:
@Service
→ 通常是Service层的组件使用的注解,Service层组件也能使用@Component@Repository
→ 通常是Dao层的组件使用@Repository注解,dao层组件也能使用@Component@Configuration
→ 配置类组件@Controller
(SpringMVC阶段)- 等
@Service
、@Repository
、@Controller
、@Configuration
,这些注解的ElementType都是TYPE,也就是这些注解都是要写在类定义上。
eg:
// @Component // 组件id的默认值是adminServiceImpl
// @Component("adminService") // 使用value属性值指定了组件id为adminService
@Service("adminService")
public class AdminServiceImpl implements AdminService{
}
- 使用
@Test
public void testFetchComponent() {
// 1. 初始化容器
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
// 2. 取出组件
AdminService adminService = applicationContext.getBean(AdminService.class);
UserService userService = applicationContext.getBean(UserService.class);
/**
* NoSuchBeanDefinitionException:没加入到容器中
*/
}
配置类中注册(JavaConfig)
@Bean
注解- 该注解增加在方法上,并且可以和其他注解共存
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {}
eg:
/**
* 在配置类中注册AdminServiceImpl组件
*
* 在配置类中写的是方法 -> 提供一个返回值类型为AdminServiceImpl的实例
* 应用程序启动的时候会加载配置类 -> 加载配置类 -> 执行配置类中的方法(记号:@Bean)
* -> 方法的返回值注册为容器的组件
*/
@Bean
public AdminService adminService() {
AdminServiceImpl adminService = new AdminServiceImpl();
return adminService;
}
/**
* 如果使用配置类中的组件,默认值为方法名
* eg:Bean("userService")
*
* 可以使用Bean注解的value方法值指令
* eg:Bean("userServiceImpl")
*/
@Bean
public UserService userService() {
return new UserServiceImpl();
}
/**
* NoUniqueBeanDefinitionException:没有唯一的Bean定义的异常
*/
/**
* @Bean对应的方法形参,默认是按照类型从容器中取出组件
*
* 如果这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
*/
@Bean
public UserService userService(@Qualifier("userDaoImpl") UserDao userDao) {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(userDao);
return userService;
}
组件注入功能(DI)
注意:要求是容器中的组件,才能够使用注入功能的注解
配置类注解中的@Bean
- 方法的形参,就是从容器中取出的组件
构造器注入
- 如果仅仅执行了一个有参构造器,实例化的时候会执行有参构造方法
- 如果有多个有参构造器,在要使用的构造器上面加上
@Autowired
eg:
@Component
public class AdminServiceImpl implements AdminService {
UserDao userDao;
String username;
/**
* 如果存在多个构造器,可以使用指定的构造器
* 使用注解 @Autowired 来指定构造器
*
*/
public AdminServiceImpl(UserDao userDao, String username) {
this.userDao = userDao;
this.username = username;
}
// 如果有无参构造方法,默认使用无参构造方法
// 如果没有无参构造方法,默认实例化时会使用有参构造方法
// 形参,默认是按照类型从容器中取出组件
// 如果形参这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
public AdminServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
}
/**
* 因为String username在容器中不存在,所有给username赋一个初始值
*/
@Autowired
public AdminServiceImpl(UserDao userDao, @Value("zhangsan") String username) {
this.userDao = userDao;
this.username = username;
}
方法注入
- 可以是组件中的任意方法,但是通常这样的方法我们用的是set方法,在方法上增加
@Autowired
注解
eg:
/**
* 方法注入
*/
@Component
public class GoodsServiceImpl implements GoodsService{
UserDao userDao;
// 形参,默认是按照类型从容器中取出组件
// 如果形参这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
成员变量注入
- 注入功能的注解使用这三组:
@Autowired
@Autowired + @Qualifier
@Resource
eg:
@Component
public class UserServiceImpl implements UserService {
// 形参,默认是按照类型从容器中取出组件
// 如果形参这个类型的组件在容器中不止一个,可以使用@Qualifier指定组件
@Autowired
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void sayHello(String name) {
System.out.println("hello " + name);
}
}
容器中注册了这些类型的组件,OrderDao
类型的组件(orderDaoImpl
)、UserDao
类型的组件(userDaoImpl1
和userDaoImpl2
)
@Repository
public class OrderDaoImpl implements OrderDao{
}
@Repository
public class UserDaoImpl1 implements UserDao{
}
@Repository
public class UserDaoImpl2 implements UserDao{
}
从容器中取出的对应的组件,执行注入,要注意,要求是在容器中的组件里注入:
/**
* 要使用注入功能注解,一定要保证当前类是容器中的组件
*/
@Service
public class UserServiceImpl implements UserService{
@Autowired //容器中该类型的组件只有一个
OrderDao orderDao;
@Autowired //使用@Qualifier注解指定组件id
@Qualifier("userDaoImpl1")
UserDao userDao1;
@Resource(name = "userDaoImpl2") //默认是按照组件的类型去注入,使用name属性指定组件id
UserDao userDao2;
}
注意:
- 开发业务代码过程中,最常用的方式只使用一个
@Autowired
:绝大部分组件在容器中这个类型的组件只有一个 - 要在容器中的组件中使用这些注解,使用注解的话,所处的类上要有组件注册功能的注解,且处于扫描包目录
Spring单元测试
- 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
<scope>test</scope>
</dependency>
- 使用
@Runwith
和@ContextConfiguration
注解,在单元测试类中可以使用注入功能的注解
eg:
@RunWith(SpringJUnit4ClassRunner.class) // 固定写法
@ContextConfiguration(classes = AppConfiguration.class) //配置类的属性
public class SpringTest {
@Autowired
CategoryService categoryService;
@Autowired
ApplicationContext applicationContext;
}
Bean
Bean的实例化
-
无参构造方法(默认方法)
-
有参构造方法
- 如果你有无参构造方法,默认使用无参构造方法
- 如果你没有无参构造方法,实例化时会使用有参构造方法
- 如果存在多个构造器,可以指定使用你标记构造器(
@Autowired
)
-
工厂
- 工厂提供实例,而实例交给Spring容器来进行管理
- 通过配置类中的方法,方法上增加
@Bean
注解,该方法的返回值注册为容器中的组件 - 在什么情况下使用的工厂的方式:
- 比如动态代理
ProxyFactoryBean
- 使用构造器要提供大量的参数,可以将这些参数封装在工厂里
- 对已有的框架或者代码进行封装
SqlSessionFactoryBean
- 比如动态代理
- 主要就是对已有的代码做些拓展
@Configuration @ComponentScan("com.coo1heisenberg,demo1") public class AppConfiguration { //这就是一种工厂 @Bean public UserService serviceProxy(UserService userService) { return ProxyUtil.getServiceProxy(userService); } }
- FactoryBean
- Spring提供的接口
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
// 这个工厂生产的方法/返回实例的方法
@Nullable
T getObject() throws Exception;
// 提供类型信息
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
eg:
@Component
public class UserFactoryBean implements FactoryBean<User> {
/**
* 完成组件的实例化
* @return 组件类型和返回值相关
* @throws Exception
*/
@Override
public User getObject() throws Exception {
User user = new User();
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
eg:
factory
目录
@Component
public class UserServiceFactoryBean implements FactoryBean<UserService> {
@Autowired
UserService instance;
//使用代理方式
@Override
public UserService getObject() throws Exception {
UserService proxy = (UserService) Proxy.newProxyInstance(
UserServiceFactoryBean.class.getClassLoader()
, UserServiceImpl.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("start");
Object invoke = method.invoke(instance, args);
System.out.println("close");
return invoke;
}
});
return proxy;
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
service
目录
@Service
public class UserServiceImpl implements UserService {
@Override
public void hello() {
// System.out.println("start");
System.out.println("hello world!");
// System.out.println("close");
}
@Override
public void goodBye() {
// System.out.println("start");
System.out.println("bye bye");
// System.out.println("close");
}
}
- 测试目录
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfiguration.class)
public class MyTest {
@Autowired
@Qualifier("userServiceFactoryBean")
UserService userService;
@Test
public void testFactoryBean() {
userService.hello(); // InvocationHandler.invoke(userService, helloMethod, null)
}
}
BeanFactory和FactoryBean之间的区别:
- BeanFactory是
ApplicationContext
的父接口,它就是容器 → 注册并管理所有的
组件(去获得容器中的组件最终都是通过getBean
方法获得) - FactoryBean注册的是
特定的
组件 →getObject
方法返回的是什么实例,注册的就是什么组件
作用域Scope
- Singleton:单例,每一次取出组件都是同一个组件
- Prototype:原型,每一次取出组件都是全新的组件
- 默认值是
singleton
,我们通常省略不写 @Scope
使用value属性指定作用域
eg:
@Component
@Scope("singleton")
public class SingletonBean {
}
@Component
@Scope("prototype")
public class PrototypeBean {
}
@Component
public class DefaultBean {
}
注册3个组件,分别给到不同的scope,然后从容器中取出组件多次,查看是否是同一个组件(查看内存地址)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfiguration.class)
public class ScopeTest {
@Autowired
SingletonBean singletonBean1; // singletonBean1:SingletonBean@2049
@Autowired
SingletonBean singletonBean2; // singletonBean2:SingletonBean@2049
@Autowired
SingletonBean singletonBean3; // singletonBean3:SingletonBean@2049
@Autowired
PrototypeBean prototypeBean1; // prototypeBean1:PrototypeBean@2050
@Autowired
PrototypeBean prototypeBean2; // prototypeBean2:PrototypeBean@2051
@Autowired
PrototypeBean prototypeBean3; // prototypeBean3:PrototypeBean@2052
@Autowired
DefaultBean defaultBean1; // defaultBean1:DefaultBean@2053
@Autowired
DefaultBean defaultBean2; // defaultBean2:DefaultBean@2053
@Autowired
DefaultBean defaultBean3; // defaultBean3:DefaultBean@2053
}
生命周期
- 生命周期:组件在容器中要完成实例化,组件从实例化开始直至可用状态会执行到哪些过程
- 组件开始执行生命周期的时间:
- 容器初始化的时候(单例组件)
prototype
类型的组件开始执行生命周期的时间:- 从容器中取出组件的时候开始执行生命周期,不取就不执行,取一次执行一次,取两次执行两次
- 对于Bean(容器中的组件),在容器中也会经历这样的一些阶段:
- 容器初始化的时候,组件做准备性工作 → 放入到容器中之前,会执行哪一些方法来准备实例
- 组件可以从容器中取出,提供服务,比如从容器中取出
userService
实例,调用其sayHello
方法 - 容器关闭,组件做销毁工作
初始化阶段的方法
- Bean的实例化(有参构造方法、无参构造方法)
- 设置参数方法(方法注入、成员变量注入)
BeanNameAware
、BeanFactoryAware
、ApplicationContextAware
- 如果从容器中取出一个
UserService
组件,这个组件是否知道它在容器中的组件名称,来自于哪里?- 容器知道,组件不知道
- 这几个
Aware
就是为了让组件本身也能获取这些信息,是接口
- 如果从容器中取出一个
BeanPostProcessor
(Bean的后处理器,这个后指的是实例化之后,其实还是在放入到容器中之前)的postProcessBeforeInitialization
(后面有初始化方法)InitializingBean
的afterPropertiesSet
方法(通常是一些框架提供的类初始化的方式)- 自定义的
init
方法(我们自己做开发的时候通常使用的方式) BeanPostProcessor
的postProcessAfterInitialization
方法- BeanPostProcessor和正在执行生命周期的组件并不是同一个,BeanPostProcessor是额外提供的,而额外提供的这个BeanPostProcessor组件。它的作用范围:除了BeanPostProcessor本身,其他的所有组件,具有通用性
eg:
@Service
public class UserServiceImpl implements UserService
, BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {
public UserServiceImpl() {
System.out.println("UserServiceImpl的实例化");
}
UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
String beanName;
@Override
public void setBeanName(String beanName) {
System.out.println("beanName = " + beanName);
this.beanName = beanName;
}
BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("通常是一些框架提供的类,它的初始化方式");
}
@PostConstruct // 在实例化之后,自定义的初始化方法
public void customInit() {
System.out.println("通常是自定义的初始化方法");
}
}
public class CommonBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行BeanPostProcessor的postProcessAfterInitialization");
return bean;
}
}
容器关闭阶段的方法
- 单例的组件才会执行到对应的方法
DisposableBean
的destroy
方法(通常是框架提供的类采用这种方式)- 自定义的
destroy
方法(通常是自定义的)
eg:
// 实现DisposableBean接口
@Override
public void destroy() throws Exception {
System.out.println("实现DisposableBean接口的destroy");
}
// 实现自定义的Destroy
@PreDestroy
public void customDestroy() {
System.out.println("自定义的destroy");
}
注意事项:
- 生命周期的方法,不是都会执行到的,有些执行是需要条件的。另外要注意
BeanPostProcessor
的作用范围