Bean基本注解开发
@Component("userDao")
@Scope("singleton")
@Lazy(false)
public class UserDaoImpl implements UserDao {
@PostConstruct
public void init(){
System.out.println("init");
}
@PreDestroy
public void destroy(){
System.out.println("destroy");
}
}
@Component
@Component作用在指定的类上并写上参数,被标注的类会自动创建放在spring容器中
同时需要在xml文件中配置组件扫描器context:component-scan并指定扫描的包名spring才能进行注解扫描工作否则就会报NoSuchBeanDefinitonException异常
值得注意的是:如果配置Component时没有指定beanName,默认的名字时当前首字母小写的类名(比如UserDaoImpl的beanName为userDaoImpl),比xml方式的全限定类名更加符合开发的规范
当一个注解的属性仅有一个且名为value时,可以在配置时不加上value=,直接写参数
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
//把当前bean对象实例化后以指定的beanName=userDao存储到spring容器中
@Component("userDao")
public class UserDaoImpl implements UserDao {
}
<!-- 注解扫描器的配置:扫描指定的基本包及其下面的子包 识别哪个类使用了@Component注解-->
<context:component-scan base-package="com.heima"></context:component-scan>
@Component的JavaEE衍生注解
这些注解已经使用了Component注解 作用是语义化帮助程序员区分
@Repository (Dao层类上使用)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
@Service (Service层类上使用)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
@Controller (Web层类上使用)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
@Scope
scopeName和value是通用的
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
@Lazy
value类型为boolean
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
boolean value() default true;
}
@PostConstruct
作用在方法上
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
@PreDestroy
作用在方法上
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}
Bean依赖注入注解开发
@Value
注入普通属性 一般用properties文件键值对方式去写 写死基本上没什么用
既可以在类中配置 也可以直接在方法中配置
public class UserDaoImpl implements UserDao {
//使用注解时是不用set方法的 直接通过反射设置值
// @Value("pickstars")
@Value("${jdbc.driver}")
private String username;
// @Value("pickstars")
public void setUsername(String username) {
this.username = username;
}
public void show(){
System.out.println("username = " + username);
}
}
⭐@Autowired
根据类型进行注入,如果同一类型的Bean有多个,则会尝试根据beanName进行匹配,如果不匹配则报错BeanDefinitionStoreException
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void show(){
System.out.println("userDao = " + userDao);
}
}
⭐@Autowired扩展点
Autowired使用在方法上时并不是根据set方法名进行注入的,而是根据参数名进行注入的
且在对象创建时会自动调用方法,如果参数的类型是集合类型指定的泛型是需要的bean类型,则会将指定包下的所有bean都注入到集合中
@Service("userService")
public class UserServiceImpl implements UserService {
// @Autowired
// @Qualifier("userDao")
// @Resource(name = "userDao")
private UserDao userDao1;
@Autowired
public void show(UserDao userDao){
System.out.println("show = " + userDao);
}
@Autowired
public void show2(List<UserDao> userDao){
System.out.println("show2 = " + userDao);
}
}
@Qualifier
结合Autowired一起使用,作用是根据名称注入相应的Bean
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDao")
private UserDao userDao1;
public void show(){
System.out.println("userDao = " + userDao1);
}
public void show1(@Qualifier("userDao2) UserDao userdao ){
System.out.println("userDao = " + userDao);
}
}
@Resource
@Resource是javax包的,并不是java本身提供了该功能,而是spring在解析时赋予一定的功能
不指定名称时根据类型注入,指定名称时根据名称注入
@Service("userService")
public class UserServiceImpl implements UserService {
// @Autowired
// @Qualifier("userDao")
@Resource(name = "userDao")
private UserDao userDao1;
public void show(){
System.out.println("userDao = " + userDao1);
}
}
非自定义Bean注解开发
⭐@Bean
非自定义Bean不能像自定义Bean一样使用@component进行管理,因为非自定义Bean一般都是封装好的,那么,非自定义Bean一般用工厂方法实例化,使用@Bean标注方法即可,@Bean的属性为BeanName,如果不指定则为当前方法名(首字母小写),工厂要加上@Component交给Spring管理,才能配置Bean
如果@Bean工厂方法需要参数的话
@Autowired根据类型自动进行Bean的匹配 可以省略
@Qualifier根据名称进行Bean的匹配
@Value根据名称进行普通数据类型匹配 可以用el表达式匹配properties文件
@Component
public class OtherBean {
@Bean
public DataSource dataSource (
@Value("${jdbc.driver}") String driver,
@Value("${jdbc.url}") String url,
@Value("${jdbc.username}") String username,
@Value("${jdbc.password}") String password,
UserService userService
){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
⭐Bean配置类的注解开发
目的是使用配置类完全去替代配置文件 怎样替代第三方标签?
@Configuration
标注当前类是一个配置类(替代配置文件的类)+@Component表示当前类交由Spring管理
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
@ComponentScan("com.heima")
组件扫描配置
替代原有xml文件的<context:component-scan base-package="com.heima">
base-package的配置方式:
- 指定一个或多个包名:扫描指定包及其子包下使用注解的类
- 不配置包名:扫描当前@ComponentScan注解配置类所在包及其子包下使用注解的类
注解扫描器的配置:扫描指定的基本包及其下面的子包 识别哪个类使用了@Component注解-
@PropertySource({"classpath:jdbc.properties"})
用于加载外部properties资源
替代原有xml文件的<context:property-placeholder location="jdbc.properties">
@import(OtherBean.Class)
加载其他配置类
替代原有xml文件的<import resource="beans.xml">
AnnotationConfigApplicationContext
使用的ApplicationContext替换为AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);不再是之前的xml方式的ApplicationContext
Spring配置其他注解
@Primary
用于相同类型的bean优先被注入使用权 是Spring3.0被引入的 与@Component和@Bean一起使用 被标注的Bean优先级会更高 则在通过类型获取Bean或通过@Autowired根据类型进行注入时 会选用优先级更高的
@Profile
⭐Spring注解的解析原理
⭐xml配置组件扫描
解析原理:在命名空间处理器中标签对应的解析器ComponentScanBeanDefinitionParser的parse方法中注册ClassPathBeanDefinitionScanner调用doScan()将扫描到配置@Component注解的类注册到BeanDefinitonMap
ClassPathXmlApplicationContext->ContextNamespaceHandler->ComponentScanBeanDefinitionParser.parse()->ClassPathBeanDefinitionScanner.doScan()
⭐注解配置组件扫描
解析原理:通过注解配置类的读取器AnnotatedBeanDefinitionReader中引用AnnotationConfigUtils工具类中注册ConfigurationClassPostProcessor,bean工厂后处理器其中注册了注解配置类的解析器,解析器的parse方法中会注册ClassPathBeanDefinitionScanner调用doScan()将扫描到配置@Component注解的类注册到BeanDefinitonMap
AnnotationConfigApplicationContext->AnnotatedBeanDefinitionReader->AnnotationConfigUtils->ConfigurationClassPostProcessor->ConfigurationClassParser->ComponentScanAnnotationParser.parse()->ClassPathBeanDefinitionScanner.doscanI()
- @Configuration注解对应的就是ConfigurationClassPostProcessor是一个bean工厂后处理器,这个处理器会实现BeanDefinitionRegistryPostProcessor用于将配置类注册到spring容器当中交由spring管理完成配置
- @Autowired注解对应的是AutowiredAnnotationBeanPostProcessor是一个bean后处理器,这个处理器实现了MergedBeanDefinitionPostProcessor处理器会对配置了@Autowired的bean在初始化过程中进行属性注入
Spring注解方式整合第三方框架
spring注解方式整合mybatis
配置DataSource SqlSessionFactoryBean和@MapperScan
DataSource和SqlSessionFactoryBean直接用@Bean工厂方式直接配置
MapperScannerConfigure使用@MapperScan注解
@Configuration
@ComponentScan("com.heima")
@PropertySource({"classpath:jdbc.properties"})
//Mapper的接口扫描
@MapperScan("com.heima.mapper")
public class SpringConfig {
@Bean
public DataSource dataSource(
@Value("${jdbc.driver}") String driver,
@Value("${jdbc.url}") String url,
@Value("${jdbc.username}") String username,
@Value("${jdbc.password}") String password){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
}
⭐@MapperScan源码解析
@MapperScan和MapperScannerConfigure的区别在于入口不同 流程大致相同
当@MapperScan被扫描加载时,回解析@Import注解,从而加载指定的MapperScannerRegistrar类,其中实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions,注册ClassPathMapperScanner调用doScan()其余步骤跟xml方式一致
⭐@Import
Spring与MyBatis注解方式有个重要的技术点就是@Import
第三方框架与spring整合xml方式很多是由第三方自己的标签完成的
第三方跨甲与spring整合注解方式很多是由@Import注解完成的
⭐ImportSelector接口
作用跟BeanFactoryPostProcessor一样 可以在selectImports将Bean注册到Be按DefinitonMap中去
spring与第三方框架集成时 可以在配置类中导入第三方框架的类或者使用注解在外面包一层跟@MapperScan一样
@Configuration
@Import(MyImportSelect.class)
public class SpringConfig {
...
}
@Override
//参数AnnotationMetadata叫做注解元数据 该对象内部封装了当前使用@Import注解的类上其他注解的元信息
public String[] selectImports(AnnotationMetadata annotationMetadata) {
MultiValueMap<String, Object> annotationAttributes = annotationMetadata.getAllAnnotationAttributes(ComponentScan.class.getName());
List<Object> packages = annotationAttributes.get("basePackages");
packages.forEach((mypackage)->{
System.out.println(mypackage);
});
System.out.println(packages);
//返回的数组封装的时需要被注册到spring容器当中的Bean的全限定名
return new String[]{OtherBean.class.getName()};
}
⭐ImportBeanDefinitionRegistrar接口
专门用于注册BeanDefiniton的接口 与BeanDefinitionRegistryPostProcessor功能一致
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
this.registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}