Spring---Annotation注解开发IOC和DI

什么是注解驱动

  • 注解概述
    注解启动时使用注解的形式替代xml配置,将繁杂的spring配置文件从工程中彻底消除掉,简化书写
  • 缺点
    1. 为了达成注解驱动的目的,可能会将原先很简单的书写,变的更加复杂
    2. XML中配置第三方开发的资源是很方便的,但使用注解驱动无法在第三方开发的资源中进行编辑,因此会增大开发工作量

比如: 自定义的类, 可以在类上加上注解,通过扫描注解,初始化该类,存到容器中.

比如: 项目中引入第三方的依赖, 要想让第三方的依赖的类, 交给spring容器来管理.需要定义一个方法,返回该对象,在方法上面加上@Bean

注解开发

步骤

  • 步骤一: 创建spring的配置类,替代配置文件,启动注解扫描,加载类中配置的注解项
/**
 * 创建一个spring的配置类,这个类的作用就是替代spring的配置文件
 * @Configuration: 指定该类是 一个配置类
 * @ComponentScan("com.service.impl"): 指定扫描的包
 */
@ComponentScan("com.service.impl")
@Configuration
public class SpringConfiguration {
}

相当于下面 xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
<!--
   spring容器扫描注解:
   通过扫描带注解的类的所在的包, 扫描带注解的类,初始化bean对象,存到容器中
   配置扫描包的书写格式:
   1. base-package="com.dao,com.service,com.web", 扫描com下的dao,service,web包
   2.base-package="com", 扫描com包以及com下面所有子包
-->
   <context:component-scan base-package="com.service.impl"></context:component-scan>
</beans>
  • 步骤二: 在所在包以及子包下面的类上面, 加上bean注解
/**
 * @Component注解作用: 定义在类上,
 * 通过spring扫描机制,如果扫描到带@Component注解的类,
 * 那么就会初始化bean( DeptServiceImpl),存到容器里面(1.根据名称存,2,根据类型存储)
 */
@Component
public class DeptServiceImpl implements DeptService {
    @Override
    public void addOne(){
        System.out.println("执行部门的添加方法11111");
    }
}
  • 测试
public class Demo {
    @Test
    public void testX3(){
        //1.创建注解容器,加载配置文件
        BeanFactory bf = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.根据名称(根据id的属性值)获取bean对象
        DeptServiceImpl bean = (DeptServiceImpl) bf.getBean("deptServiceImpl");
        bean.addOne();
    }
    @Test
    public void testX2(){
        //1.创建注解容器,加载配置文件
        BeanFactory bf = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.获取bean对象
        DeptServiceImpl bean = bf.getBean(DeptServiceImpl.class);
        bean.addOne();
    }
    @Test
    public void testX(){
        //1.创建容器,加载配置文件
        BeanFactory bf = new ClassPathXmlApplicationContext("applicaitonContext.xml");
        DeptServiceImpl bean = bf.getBean(DeptServiceImpl.class);
         bean.addOne();
    }
}

小结

  • 在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描

  • 扫描过程是以文件夹(包结构其实就是文件夹)递归迭代的形式进行的

  • 扫描过程仅读取合法的java文件对带注解的java类进行初始化, 初始化存到容器里面(常见1.根据名称存储 2. 根据class来存储)

  • 扫描结束后会将可识别的有效注解转化为spring对应的资源加入IOC容器

注意

  • 无论是注解格式还是XML配置格式,最终都是将资源加载到IoC容器中,差别仅仅是数据读取方式不同

  • 从加载效率上来说注解优于XML配置文件

模拟Spring容器基于注解开发,初始化bean对象,存到容器中

  • 步骤一: 定义注解 @MyComponent, @MyConfiguration
@Target(ElementType.TYPE)//限定注解的作用位置, 在类上上面
@Retention(RetentionPolicy.RUNTIME)//指定注解的保留时长,设置注解一直活到运行时
public @interface MyComponent {
    String value() default "";//指定bean的名称
}

@Target(ElementType.TYPE)//限定注解的作用位置, 在类上上面
@Retention(RetentionPolicy.RUNTIME)//指定注解的保留时长,设置注解一直活到运行时
public @interface MyConfiguration {//注解类: 作用配置扫描的包
    String value();//配置扫描的包
    //String[] value();
    //String[] basePackages();
}
  • 步骤二: 定义一个测试类, 类上面加上自定义的注解@MyComponent
@MyConfiguration("system.my.bean")
public class MySpringConfig {//定义配置类
}

//使用自定义的注解,通过value属性设置bean的名称
@MyComponent("demoService")
public class DemoService {//定义一个bean类测试
    public  void testService(){
        System.out.println("执行业务层的方法");
    }
}
  • 步骤三: 自定义注解的容器对象: 1. 引入配置类,读取配置类上面扫描的包 2. 递归调用包文件夹,获取java类
/**
 * 在进行包所扫描时,会对配置的包及其子包中所有文件进行扫描
 * 扫描过程是以文件夹(包结构其实就是文件夹)递归迭代的形式进行的
 * 扫描过程仅读取合法的java文件
 * ​对带注解的java类进行初始化, 初始化存到容器里面(常见1.根据名称存储  2. 根据class来存储)
 * 扫描结束后会将可识别的有效注解转化为spring对应的资源加入IoC容器
 */
public class MyAnnotationContainer {
    //0.定义容器
    //map的key: bean的名称或者bean的class对象
    //map的value: bean的对象
    private Map<Object,Object> container = new ConcurrentHashMap<Object,Object>();
    //1.引入注解的配置类
    private Class configClass;
    //2.通过构造方法给注解的配置类赋值
    public MyAnnotationContainer(Class configClass){
        //3.给配置类赋值
        this.configClass = configClass;//MySpringConfig
        //4.通过配置类,获取配置注解 MySpringConfig
        boolean flag = configClass.isAnnotationPresent(MyConfiguration.class);
        if(flag){
            //说明MySpringConfig类上面有注解: MyConfiguration
            MyConfiguration annotation =(MyConfiguration)configClass.getAnnotation(MyConfiguration.class);
            //获取MyConfiguration配置的包
            String packageName = annotation.value();//system.my.bean.son
            //递归调用包里面的所有的java文件,获取到包下面的所有类,判断类上面是否有 MyComponent
            scanAnnotationCreateBeanTOMpa(packageName);
        }

    }
    //定义一个方法: 获取包下面的所有类,判断类上面是否有注解 MyComponent ,如果有初始bean,存到容器中
    public  void  scanAnnotationCreateBeanTOMpa(String packageName){
        //1.解析包.//修改包结构变成文件夹system/my/bean/son
        String packagePathName= packageName.replace(".","/");
        try {
            //2.通过类加载器获取包的绝对路径(带盘符的)
            Enumeration<URL> resources =  MyAnnotationContainer.class.getClassLoader().getResources(packagePathName);
            //3.获取包下面的所有java文件: class文件
            Set<String>  classNames=null;
            while (resources.hasMoreElements()){
                URL url = resources.nextElement();//拿到枚举类里面保存的文件的绝对路径
                String filePath = url.getFile();
                classNames = FileUtils.getAllFilesName(new File(filePath));
            }
            //4.根据class文件,创建bean对象,存到容器中
            if(classNames!=null && classNames.size()>0){
                //5.判断bean是否带有MyComponent
                for (String className : classNames) {
                      //className: DemoMapper.class,DemoService.clas
                    // 截取class,获取类名DemoMapper,DemoMapper
                    String typeName= className.substring(0,className.lastIndexOf("."));
                   //拼接类的全路径: 包名+类名
                    String typeAllName = packageName+"."+typeName;
                    //利用反射: 得到Class
                    Class clz = Class.forName(typeAllName);
                    //判断该类上面是否有MyComponent
                    //DemoMapper, DemoService上面是有注解MyComponent
                    boolean flag = clz.isAnnotationPresent(MyComponent.class);
                    if(flag){
                        //获取类上面的注解对象
                        MyComponent myComponent =(MyComponent) clz.getAnnotation(MyComponent.class);
                        //获取注解里面配置的bean的名称
                        String beanName = myComponent.value();
                        //利用无参数的构造方法创建bean对象
                        Object beanObj = clz.newInstance();
                        //根据名称添加到容器中
                        container.put(beanName,beanObj);
                        //根据类型存添加到容器中
                        container.put(clz,beanObj);
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //3.根据名称从容器中获取bean对象
    public  Object  getBeanForName(String name){
        return  container.get(name);
    }
    //4.根据class类型,从容器中获取bean对象
    public <T> T getBeanForClass(Class<T> clz){
        Object o = container.get(clz);
        T bean = (T) o;
        return bean;
    }
    
}

引用类型注解

引用类型注解— 自定义类被初始化bean对象的注解

  • 自定义bean类,初始化对象的注解
    注解名称:@Component, @Controller, @Service ,@Repository
    在这里插入图片描述
  • bean的作用域的注解
    注解名称 @Scope
    在这里插入图片描述
  • bean的生命周期的注解
    注解的名称@PostConstruct @PreDestroy
    在这里插入图片描述

引用类型注解—加载第三方资源, 一般是第三方框架里面bean类,

注解名称为@Bean
在这里插入图片描述
注意: 第三方资源如果被其它地方引用, 需要添加value属性,指定bean的名称

注入

非引用类型注入

注解的名称为@Value
在这里插入图片描述

引用类型属性注入

实现方式有两种

  • 方式一: @Autowired(可以单独使用)、@Qualifier(不能单独使用,必须配合Autowired使用)
  • 方式二: @Resource,是JSR250(JAVAEE)规范中的注解,可以简化书写格式
    @Resource相关属性
    • name:设置注入的bean的id
    • type:设置注入的bean的类型,接收的参数为Class类型
  • 方式一和方式二的区别
    在这里插入图片描述
    @Resource,默认根据名称从容器中取出bean,如果没有根据类型去取
  • 细节
    在这里插入图片描述
  • 其他
    在这里插入图片描述

常用注解

  • 加载外部的properties文件注解
    在这里插入图片描述
  • 扫描注解和替代文件注解
    注意: 纯注解开发的话, 使用AnnotationConfigApplicationContext 注解容器对象
    在这里插入图片描述
  • 导入第三方Bean的注解
    注意: @Import通常和@Configuration一块使用(加上@Configuration目的是被spring扫描到)
    在这里插入图片描述

Spring注解总结

  • Spring原始注解, 主要是替代bean标签的配置
注解说明
@Component使用在类上用于实例化Bean
@Controller使用在web层类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用用于根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

注意:
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

<!--注解的组件扫描-->
<context:component-scan base-package="com"></context:component-scan>
  • 使用@Compont或@Repository标识UserDaoImpl需要Spring进行实例化。
//@Component("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
    	System.out.println("save running... ...");
    }
}
  • 使用@Compont或@Service标识UserServiceImpl需要Spring进行实例化
  • 使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
    /*@Autowired
    @Qualifier("userDao")*/
    @Resource(name="userDao")
    private UserDao userDao;
    @Override
    public void save() {       
   	  userDao.save();
    }
}
  • 使用@Value进行字符串的注入
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Value("注入普通数据")
    private String str;
    @Value("${jdbc.driver}")
    private String driver;
    @Override
    public void save() {
        System.out.println(str);
        System.out.println(driver);
        System.out.println("save running... ...");
    }
}
  • 使用@Scope标注Bean的范围
//@Scope("prototype")
@Scope("singleton")
public class UserDaoImpl implements UserDao {
   //此处省略代码
}
  • 使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法
@PostConstruct
public void init(){
	System.out.println("初始化方法....");
}
@PreDestroy
public void destroy(){
	System.out.println("销毁方法.....");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值