基于注解的spring应用

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) {
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值