配置Bean
Spring在3.x提供的新的注解,用于替换XML配置文件。
@Configuration
public class AppConfig{
}
- 配置形式发生了变化:由xml转成java代码,由@Configuration,这个类就等同于一个applicationContext.xml这个配置文件
- 工厂的转变。由原来的ClassPathXmlApplicationContext转换成AnnotationConfigApplicationContext或者AnnotationConfigApplicationContext类。
@Configuration的本质是@Component注解的衍生注解。可查看源码。
@Bean注解
@Bean注解在配置bean中进行使用,等同于xml配置中的
标签。
对象的创建
首先来会回顾下创建对象:
- 简单对象:直接能够通过new的方式创建的对象
- 复杂对象:不能直接通过new的方式创建的,比如Connection,SqlSessionFactory。
所有创建简单对象不就是直接注入:
@Configuration
public class AppConfig {
@Bean
public User user(){
return new User();
}
等同于
<bean id="user" class="com.bearjun.entity"/>
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
User user = (User) ctx.getBean("user");
System.out.println(user);
}
创建复杂对象还不是一样,需要什么,我们就返回什么
@Bean
public Connection conn(){
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/users?useSSL=false","root","123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
// 获取bean的时候:
Connection conn = (Connection) ctx.getBean("conn");
但是前面不是说过吗?一些遗留系统用的是FactoryBean的方式创建复杂对象,那又怎么操作呢?
// 通过FactoryBean来创建复杂对象
public class ConnectionFactoryBean implements FactoryBean<Connection> {
public Connection getObject() throws Exception {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/users?useSSL=false","root","123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public Class<?> getObjectType() {
return Connection.class;
}
public boolean isSingleton() {
return false;
}
}
//通过配置Bean整合这个FactoryBean创建的对象
@Bean
public Connection conn1() throws Exception {
return new ConnectionFactoryBean().getObject();
}
如果想通过@Bean注解指定指定对象的id值,只需要在@Bean("id")即可。
控制对象的创建次数
用@Bean中,我们怎么像xml中控制对象创建次数呢?
<bean id="user" class="com.bearjun.entity" scope="singleton|prototype"/>
// @Scope相当于bean标签中的scope
@Bean("u")
@Scope("singleton|prototype")
public User user(){
return new User();
}
当Spring创建对象的形式是默认形式(singleton),在工厂创建的时候,对象会同时被创建出来,如果你想在getBean的时候帮你创建这个单例对象,你只需要配置懒加载就可以,也就是lazy-init为true
当Spring创建对象的形式是prototype形式,一定只是在你getBean的时候工厂才帮你创建对象,而此时你无论配置什么懒加载,都不会生效。
其实原因很简单,你创建单例对象,整个工厂这个对象就一份,出于Spring的角度考虑,其初始化创建好了你直接拿来用,或者你想用的时候spring给你造一个。
注入
- 用户自定义类型的注入
我们自己写的业务service或者dao。
// 基于配置文件的注入
<bean id="userService" class="com.chenxin.spring5.config.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
<bean id="userDao" class="com.chenxin.spring5.config.UserDaoImpl"></bean>
// 基于@bean注解的注入
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
// userDao()直接调用上述的方法,或者给本方法添加UserDao的参数。
userService.setUserDao(userDao());
return userService;
}
- JDK类型的注入
对User的基本的成员变量进行赋值:
@Bean
public User user() {
User user = new User();
user.setName("bear");
user.setPassword("123456");
return user;
}
上述的userName和password都是写死的,也就是出现了耦合,怎么解决的呢?
定义一个配置文件application.properties,后写两个key,value,,结合上节的@PropertySource。
@Configuration
@PropertySource("application.properties")
public class AppConfig {
@Value("${name}")
private String name;
@Value("${password}")
private String password;
@Bean
public User user() {
User user = new User();
user.setName(name);
user.setPassword(password);
return user;
}
}
@ComponentScan注解
作用:等同于XML配置文件中的<context:component-scan />标签
目的:进行相关注解的扫描 (@Component @Value @Autowired . . .)
基本使用
//加载配置Bean上:
@Configuration
// 扫描注解所在的包路径
@ComponentScan(basePackages = "com.bearjun.scan")
public class AppConfig2 {
}
<context:component-scan base-package=""/>
排除、包含的使用
- 排除过滤
<context:component-scan base-package="com.bearjun">
<context:exclude-filter type="assignable" expression="com.bearjun.entity.User"/>
</context:component-scan>
@Configuration
@ComponentScan(basePackages = "com.chenxin.spring5",
excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {User.class})})
public class AppConfig2 {
}
- 包含过滤
<context:component-scan base-package="com.bearjun" use-default-filters="false">
<context:include-filter type="" expression=""/>
</context:component-scan>
@ComponentScan(basePackages = "com.bearjun",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type=
FilterType.ANNOTATION,value={Service.class})})
不管是那种策略,其中的值还是要注意下:
- type = FilterType.ANNOTATION 对应 value
- type = FilterType.ASSIGNABLE_TYPE 对应 value
- type = FilterType.ASPECTJ 对应 pattern
- type = FilterType.REGEX 对应 pattern
- type = FilterType.CUSTOM 对应 value
Spring工厂创建对象的方式分析
多种配置方式的应用场景
- @Component注解,其各种衍生注解标注。【推荐】
- @Bean注解,其他框架提供的类型。【推荐】
- 传统的xml注入,遗留系统的整合。
- @Import注入,springboot底层或者多配制bean的整合。
配置优先级
@Component及其衍生注解 < @Bean < 配置文件bean标签
说明:优先级高的配置会覆盖优先级低配置。
@Component及其衍生注解 < @Bean < 配置文件bean标签
优先级高的配置会覆盖优先级低配置
@Component
public class User{
}
@Bean
public User user(){
return new User();
}
<bean id="user" class="xxx.User"/>
配置覆盖条件:id值保持一致。
解决基于注解进行配置的耦合问题
应用场景:一个实现类,后期需要修改。在不修改代码的情况下。
<!-- applicationContext.xml 配置文件中进行覆盖 -->
<bean id="userDAO" class="com.bearjun.injection.UserDAOImplNew"/>
@Configuration
//@ImportResource("applicationContext.xml")
public class AppConfig4 {
@Bean
public UserDAO userDAO() {
return new UserDAOImpl();
}
}
// 重新创建一个配置文件,然后引入xml依赖,覆盖之前的配置。
@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig5{
}
整合多个配置信息
问题一:为什么会有多个配置信息?
- 拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想。
问题二:多配置信息整合的方式?
- 多个配置Bean的整合。
- 配置Bean与@Component相关注解的整合。
- 配置Bean与SpringXML配置文件的整合。
问题三:整合多种配置需要关注那些要点?
- 如何使多配置的信息 汇总成一个整体。
- 如何实现跨配置的注入。
多个配置Bean的整合
- base-package进行多个配置Bean的整合
当然,在工厂创建时,指定多个配置Bean的Class对象。同时加载多个配置文件的class。
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class,AppConfig2.class);
- 通过 @Import(xxx.class)
把AppConfig1当作主配置Bean,在AppConfig1上添加@Import(Appconfig2.class),完成整合。
那么问题来了:如何跨配置进行注入?
直接通过@Autowired直接,把需要的对象作为本配置Bean的属性。
@Configuration
@Import(AppConfig2.class)
public class AppConfig1 {
//把要跨配置注入的对象作为本配置Bean的属性
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
@Configuration
public class AppConfig2 {
@Bean
public UserDAO userDAO() {
return new UserDAOImpl();
}
}
配置Bean与@Component相关注解的整合
通过@ComponentScan(basePackages = " ")注解整合:
@Component(@Repository)
public class UserDAOImpl implements UserDAO{
}
@Configuration
@ComponentScan("")
public class AppConfig3 {
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class);
配置Bean与配置文件整合
使用 @ImportResource(“applicationContext.xml”) 注解整合
//1. 遗留系统的整合 2. 配置覆盖
public class UserDAOImpl implements UserDAO{
}
<bean id="userDAO" class="com.baizhiedu.injection.UserDAOImpl"/>
@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig4 {
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService() {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig4.class);
配置Bean底层实现原理
Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,来进行对象相关的配置、处理。
四维一体的开发思想
问题一:什么是四维一体?
Spring开发一个功能的4种形式,虽然开发方式不同,但是最终效果是一样的。
- 基于schema
- 基于特定功能注解
- 基于原始<bean
- 基于@Bean注解
四维一体的开发案例:
- <context:property-placehoder
- @PropertySource 【推荐】
-
- @Bean 【推荐】
评论区