由于准备着手研究一下Spring Boot 和Spring Cloud,所以抽空把Spring对Bean的装配和Spring容器的依赖注入重新整理了一下,理解了一下内在本质!
今天主要整理的是基于注解的方式对Bean的管理,下面是具体的Demo!
Maven依赖:
<!-- Spring Context 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!-- JSR 330 依赖 -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
Demo01:
//通过加入组建
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class, User.class, UserDao.class,UserService.class,UserController.class);
//通过扫描包
//AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.harry.**");
注: 首先获取Spring容器applicationContext,这里通过添加配置类组建的方式或者扫描包的方式来让Spring对Bean进行装配,接着通过容器才对Bean进行一系列操作。
MyConfig.Java
package com.harry.spring4.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
/**
* Bean的配置类,类似于applicationContext.xml里配置的各种Bean
*
* @author Harry Wan
*
*/
@Configuration
public class MyConfig {
/**
* 表示配置了一个Bean,name默认为方法名 "createMyBean"
*
* @return
*/
@Bean
@Scope(scopeName = "prototype")
public MyBean createMyBean() {
return new MyBean();
}
/**
* 自定义Bean的name,从容器中获取时按定义的名称获取
*
* @return
*/
@Bean(name = "myBean")
@Scope(scopeName = "prototype")
public MyBean createMyBeanWithRename() {
return new MyBean();
}
@Bean
public MyFactoryBean createMyFactoryBean() {
return new MyFactoryBean();
}
@Bean
public MyCustomFactory createMyCustomFactory() {
return new MyCustomFactory();
}
@Bean
public MyBean createMyBeanByCustomFactory(MyCustomFactory factory) {
return factory.createMyBean();
}
@Bean
public MyBean2 createMyBean2ByCustomFactory(MyCustomFactory factory) {
return factory.createMyBean2();
}
@Bean
public MyBean3 createMyBean3() {
return new MyBean3();
}
@Bean(initMethod = "init", destroyMethod = "destroy")
public MyBean4 createMyBean4() {
return new MyBean4();
}
@Bean
public MyBean5 createMyBean5() {
return new MyBean5();
}
@Bean
//@Primary
public UserDao createUserDao(){
return new UserDao();
}
}
注: 这里通过@Configuration注解表示当前为配置类,类似于项目中的applicationContext.xml,同时通过@Bean来标识自定义实例化的Bean
实例化Bean的四种方式:
第一种:
/**第一种方式: 通过自定义创建实例化Bean,交给Spring来管理**/
//从容器中通过类型来获取.
//注: 若容器中有多个实例化Bean的方法,此时通过类型来获取,会Bean匹配错误
//System.out.println(applicationContext.getBean(MyBean.class));
//从容器中通过名称来获取
System.out.println(applicationContext.getBean("createMyBean"));
System.out.println(applicationContext.getBean("myBean"));
配置:
/**
* 表示配置了一个Bean,name默认为方法名 "createMyBean"
*
* @return
*/
@Bean
@Scope(scopeName = "prototype")
public MyBean createMyBean() {
return new MyBean();
}
/**
* 自定义Bean的name,从容器中获取时按定义的名称获取
*
* @return
*/
@Bean(name = "myBean")
@Scope(scopeName = "prototype")
public MyBean createMyBeanWithRename() {
return new MyBean();
}
注:在配置类中自定义实例化Bean方法,并且使用@Bean注解标识,交给Spring管理,最后通过容器获取Bean实例.在通过类型获取时,如果在容器中有多个实例化Bean的方法,Spring会报匹配错误,在通过名称获取时,如果不自定义@Bean(name = "myBean"),默认的名称为实例化Bean的方法名applicationContext.getBean("createMyBean")
第二种:
/** 第二种方式: 通过BeanFactory来创建实例化Bean**/
//通过类型获取FactoryBean创建的Bean的实例对象
//System.out.println(applicationContext.getBean(MyBean2.class));
//通过名字来获取FactoryBean创建的Bean的实例对象
System.out.println(applicationContext.getBean("createMyFactoryBean"));
//通过类型获取到的是MyFactoryBean本身,并非MyFactoryBean创建出来的Bean
System.out.println(applicationContext.getBean(MyFactoryBean.class));
//通过名称获取到的是MyFactoryBean本身,并非MyFactoryBean创建出来的Bean
System.out.println(applicationContext.getBean("&createMyFactoryBean"));
MyFactoryBean.java
package com.harry.spring4.demo;
import org.springframework.beans.factory.FactoryBean;
/**
* 通过FactoryBean 来创建对象
* @author Harry Wan
*
*/
public class MyFactoryBean implements FactoryBean<MyBean2> {
//获取到FactoryBean所创建的实力对象
public MyBean2 getObject() throws Exception {
return new MyBean2();
}
//获取Bean的类型
public Class<?> getObjectType() {
return MyBean2.class;
}
public boolean isSingleton() {
return true;
}
}
注:这里通过实现FactoryBean接口传入需要实例化的Bean类型,实现对应的方法,再将我们自定义的MyFactoryBean添加到配置类中,最终来获取我们需要实例化的Bean对象。
由于FactoryBean也可以抽象成Bean的一种,所以在获取时要区分我们需要的目标Bean对象和FactoryBean对象
第三种:
/** 第三种方式: 通过自定义Factory来创建Bean实例,把自定义的Factory也当作Bean交给Spring来管理 **/
//可以通过类型来获取MyBean.class,为了避免通过类型匹配到多个实例化Bean的方法,改为通过Name来获取
System.out.println(applicationContext.getBean("createMyBeanByCustomFactory"));
System.out.println(applicationContext.getBean("createMyBean2ByCustomFactory"));
自定义实例化Bean的工厂类MyCustomFactory.java:
package com.harry.spring4.demo;
public class MyCustomFactory {
public MyBean createMyBean() {
return new MyBean();
}
public MyBean2 createMyBean2() {
return new MyBean2();
}
}
配置类MyConfig.java:
@Bean
public MyCustomFactory createMyCustomFactory() {
return new MyCustomFactory();
}
@Bean
public MyBean createMyBeanByCustomFactory(MyCustomFactory factory) {
return factory.createMyBean();
}
@Bean
public MyBean2 createMyBean2ByCustomFactory(MyCustomFactory factory) {
return factory.createMyBean2();
}
注: 这里将自定义Bean的工厂类加入到Spring容器中进行管理,在实例化目标Bean时,对于需要的工厂类参数,Spring会自动去匹配当前容器中已经装配好的对应工厂对象进行传入
第四种:
/**第四种方式: 通过使用@Component组件相关注解,交给Spring容器装配**/
//注: 实际开发中通常使用scan扫描包自动加入Spring容器(new AnnotationConfigApplicationContext("com.harry.**");),如若其他方式要手动加入到Spring容器中
System.out.println(applicationContext.getBeansOfType(User.class));//获取Spring容器管理Bean时该Bean使用的默认名称(Map: 名称 = 实例化对象)
//@Component
System.out.println(applicationContext.getBean(User.class));
System.out.println(applicationContext.getBean("myUser"));
//@Repository
System.out.println(applicationContext.getBean(UserDao.class));
//@Service
System.out.println(applicationContext.getBean(UserService.class));
//@Controller
System.out.println(applicationContext.getBean(UserController.class));
注:通过使用@Component等注解表示当前类为Spring的组建,Spring默认进行装配进行管理,这也是实际开发中用的最多的方式
//---------------------------------------------------------这是一道华丽的分界线------------------------------------------------------
实现Bean在被Spring容器加载之后和被销毁之后自定义添加业务逻辑的三种方式
第一种:
/**第一种方式: 目标Bean实现InitializingBean, DisposableBean接口,实现对应的afterPropertiesSet()和destroy()方法**/
System.out.println(applicationContext.getBean("createMyBean3"));
package com.harry.spring4.demo;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class MyBean3 implements InitializingBean, DisposableBean {
/**
* 当Bean被Spring加载之后会执行
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("==It will be executed after being Loaded==");
}
/**
* 当Bean销毁之后会执行
*/
@Override
public void destroy() throws Exception {
System.out.println("==It will be executed after being Destroyed==");
}
}
注:目标Bean通过实现InitializingBean, DisposableBean接口,实现对应的afterPropertiesSet()和destroy()方法
第二种
/**第二种方式: 在注解@Bean是指定(initMethod = "init", destroyMethod = "destroy")**/
System.out.println(applicationContext.getBean("createMyBean4"));
配置类:
@Bean(initMethod = "init", destroyMethod = "destroy")
public MyBean4 createMyBean4() {
return new MyBean4();
}
目标Bean:
package com.harry.spring4.demo;
public class MyBean4 {
/**
* 当Bean被Spring加载之后会执行
*/
public void init() {
System.out.println("==It will be executed after being Loaded==");
}
/**
* 当Bean销毁之后会执行
*/
public void destroy(){
System.out.println("==It will be executed after being Destroyed==");
}
}
注:这里通过在配置类注解@Bean中标注initMethod , destroyMethod方法来实现,这也是实际开发中最常用的方式,基于Spring提供的@Bean注解的配置
第三种
/**第三种方式: 通过Javax(JSR-250)注解@PostConstruct和@PreDestroy方式**/
System.out.println(applicationContext.getBean("createMyBean5"));
目标Bean:
package com.harry.spring4.demo;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyBean5 {
/**
* 当Bean被Spring加载之后会执行
*/
@PostConstruct
public void init() {
System.out.println("==It will be executed after being Loaded==");
}
/**
* 当Bean销毁之后会执行
*/
@PreDestroy
public void destroy(){
System.out.println("==It will be executed after being Destroyed==");
}
}
注:通过使用Javax注解@PostConstruct和@PreDestroy实现
//---------------------------------------这是一道华丽的分界线----------------------------------------------------
注入容器的三种方式
package com.harry.spring4.demo;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Repository;
/**
* Spring 依赖注入
* @author Harry Wan
*
*/
public class MyDemo2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.harry.**");
//使用@Repository注解装配
System.out.println(applicationContext.getBean("userDao"));
//使用@Bean 自定义实例化对象装配
System.out.println(applicationContext.getBean("createUserDao"));
/**注入的三种方式**/
//@Autowired 依赖 UserDao ---Spring提供的注入方式,可以指定具体装配哪一个
//@Resource 依赖User ----JSR-250提供的注入方式(JDK自带)
//@Inject 依赖MyBean ----JSR-330提供的注入方式(需要添加依赖)
UserService userService = applicationContext.getBean(UserService.class);
//在有多个实例化Bean的情况下,默认情况下,Spring优先会注入@Repository注解的组建Bean,
//可以在@Autowired下使用@Qualifier(value = "createUserDao")或者在@Bean下使用@Primary来指定装配哪一个
System.out.println(userService);
applicationContext.close();
}
}
package com.harry.spring4.demo;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
@Qualifier(value="createUserDao")
private UserDao userDao;
//JSR-250提供的注入方式(JDK自带)
@Resource
private User user;
//JSR-330提供的注入方式(需要添加依赖)
@Inject
private MyBean myBean;
@Override
public String toString() {
return "UserService [userDao=" + userDao + ", user=" + user + ", myBean=" + myBean + "]";
}
}
@Bean
//@Primary
public UserDao createUserDao(){
return new UserDao();
}
注:第一种方式: 最常用的方法,使用Spring提供的@Autowired注解,当容器中有多个依赖注入的Bean对象时,可以使用
@Qualifier(value = "createUserDao")或者在@Bean下使用@Primary来指定装配哪一个
第二种方式:使用JSR-250提供的注入方式(JDK自带),基于注解@Resource依赖注入
第三种方式:使用JSR-330提供的注入方式(需要添加依赖), 基于注解@Inject实现依赖注入
//-------------------------------这是一道华丽的分界线----------------------------------------------------
基于注解设置在Spring扫描包时自动忽略相应Bean的装配:
package com.harry.spring4.demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.stereotype.Service;
public class MyDemo3 {
public static void main(String[] args) {
/**
* 把扫描包的注解配置信息添加在AnnotationScanConfig中,通过Spring容器读取配置文件的方式加载扫描配置
* @ComponentScan(basePackages="com.harry.**",excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService.class,MyConfig.class}))
* 排除用@Service注解的UserService和在MyConfig中自定义配置的Bean的扫描
*
* 如若使用@Component注解注释的Bean,可直接排除
* 如若使用Config配置文件自定义实例化Bean的方式,不能直接排除Bean的本身,要填写Config配置类,从而来排除配置文件中配置的Bean
*
*/
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationScanConfig.class);
System.out.println(applicationContext.getBean(User.class));
System.out.println(applicationContext.getBean(UserService.class));
System.out.println(applicationContext.getBean(MyBean.class));
applicationContext.close();
}
}
扫描配置AnnotationScanConfig.java
package com.harry.spring4.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(basePackages="com.harry.**",excludeFilters=@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={UserService.class,MyConfig.class}))
public class AnnotationScanConfig {
}
注: 使用@Configuration表示当前为以配置类加入到Spring容器中, @ComponentScan表示Spring扫描的配置信息,通过指定excludeFilters来过滤相应Bean的装配,FilterType.ASSIGNABLE_TYPE表示我们传入指定的要忽略的目标Bean的类型或者Bean所在的配置类。
如果Bean使用的是@Component等注解标识,可以直接在Filter中指定该Bean的类型,从而忽略装配
如果Bean使用的是配置类的方式,在配置类中使用@Bean的注解自定义实例化Bean,我们需要将配置类加到Filter中,从而对该Bean进行忽略,若加Bean本身,而非其Bean所在的配置类,则达不到忽略的效果
最后,由于Bean都是默认以单例的形式装配在Spring容器中,我们可以通过@Scope(scopeName = "prototype")来标识,表示每次从容器中获取该Bean是都是重新实例化一个新的对象。
Demo运行示例: