XML配置对比注解
注解开发定义bean
使用@Component定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao {
}
核心配置文件中通过组件扫描加载bean
//注意对应component
<context:component-scan base-package="com.learn"/>
Spring提供@Component注解的三个衍生注解
@Controller:用于表现层bean定义
@Controller("bookController")
public class BookController {
}
@Service:用于业务层bean定义
@Service("bookService")
public class BookServiceImpl implements BookService {
}
@Repository:用于数据层bean定义
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
}
Spring3.0 纯注解开发模式
@Configuration
用Java类代替Spring核心配置文件
@Configuration 注解用于设定当前类为配置类
@ComponentScan 注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式**@PropertySource** 注解用于加载对应的properties文件
@
创建一个Java类SpringConfig 添加注解
@Configuration
@ComponentScan({"com.learn.dao","com.learn.service"})
@PropertySource("jdbc.properties")
public class SpringConfig {
}
读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器
//加载配置文件 初始化容器
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类 初始化容器
ApplicationContext ctx=new AnnotationConfigApplicationContext(SpringConfig.class);
对于第三方的bean方法使用时需要在config中配置bean
使用 @Bean 配置第三方bean
@Configuration
@ComponentScan("com.learn")
@PropertySource({"jdbc.properties"})
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource druidDataSource=new DruidDataSource();
druidDataSource.setDriverClassName("${jdbc.driver}");
druidDataSource.setUrl("${jdbc.url}");
druidDataSource.setUsername("${jdbc.username}");
druidDataSource.setPassword("${jdbc.password}");
return druidDataSource;
}
@PropertySource 用于确定需要加载的properties配置文件,多个配置文件用用数组格式
@Import 注解手动加入配置类到核心配置,此注解只能添加一次,多个数据用数组格式
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig {
}
第三方bean的依赖注入
简单类型注入
@Configuration
@ComponentScan("com.learn")
@PropertySource({"jdbc.properties"})
public class JdbcConfig {
@Value("${studentName}")
private String studentName;
@Bean
public DataSource dataSource(){
System.out.println(studentName);
}
引用类型依赖注入
只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象
@Bean
public DataSource dataSource(BookService bookService){
System.out.println(bookService);
...
}
@Scope
bean的作用范围
使用@Scope定义bean的作用范围
singleton 为单例的
prototype 为非单例的
@Repository("bookDao")
@Scope("singleton")
public class BookDaoImpl implements BookDao {
}
使用**@PostConstruct** @PreDestroy 来定义bean的生命周期
@PostConstruct
public void init(){
System.out.println("init...");
}
//注意 仅当bean为单例的时候destroy()才会调用 非单例时方法不执行
@PreDestroy
public void destroy(){
System.out.println("destroy...");
}
@Autowired/@Value
使用**@Autowired** 注解开启自动装配模式(按类型)
@Service("bookService")
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
}
注意事项
自动装配基于反射创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法
自动装配建议使用 无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法
使用 @qualifier 注解开启指定名称装配bean
@qualifier无法单独使用必须配合@Autowired注解使用
@Service("bookService")
public class BookServiceImpl implements BookService {
@Autowired
@qualifier("bookDao")
private BookDao bookDao;
}
使用 @Value 实现简单类型注入
@Service("bookService")
public class BookServiceImpl implements BookService {
@Value("propertiesValue")
private String name;
}
@Value还能用来做装配对象成员,即和@Autowired一样的作用。例如:
@Configuration
public class MyBean {
@Bean
public User getUser() {
User user = new User();
user.setName("LJJ");
return user;
}
}
然后通过@Value装配:
@Value("#{getUser}")
private User user;
@Value("Hello")
private String name;
@Value("#{getUser.name}")
private String username;
@GetMapping("/hello")
@ResponseBody
public String hello() {
return user.getName();
}
自定义一个Bean:
关于@Value的注意事项
总结下就是:
对于 $ {} 这样的占位符,值的注入本质上就是字符串的替换操作。
对于 # {} 这样的占位符。值的注入本质上就是SpEL表达式的计算。
对于 $ {} 的原理。先看这个字段上是否有@Value注解,如果有拿到它的占位符。
如果这个占位符是字符串,就会进行对应的解析。去掉 $ {},拿中间的部分,例如本案例中的username。
根据username去整个系统的数据源中寻找有相同名称的键值对 。返回第一个找到的值。
由于系统环境变量数据源OriginAwareSystemEnvironmentPropertySource {name=‘systemEnvironment’},遍历顺序优先于OriginTrackedMapPropertySource {name=‘applicationConfig: [classpath:/application.properties]’}这类外部数据源配置。
而系统变量数据源恰好有username这个属性,因此提前返回。导致最终注入的值和我们预想的不一致。
因此实际开发过程中,我们需要根据业务、场景、层级关系等因素,命名具有一定规范的变量名,即可大大降低这类自定义命名和系统变量命名重复的概率。