框架技术--- Spring注解DI、AOP介绍

Spring

Javaweb —Spring


SpringIoc注解方式注入、Aop引入


昨天已经大概分享了Spring的介绍,Spring是一个大的技术框架,其中有很多的功能模块,两大核心的模式就是IOC和AOP;IOC就是控制反转; 创建和管理对象的权力交给Spring容器applicationContext;但是实体类对象不需要,因为要存到数据库中,数据;Spring中IOC实现方式是DI,第一种是配置文件applicationContext.xml中实现的DI,还有就是注解的方式实现

IOC的依赖管理很简单,比如之前的Student依赖School;就是要在Student中使用School类型的对象;那么就直接在Student的对象中绑定School【通过ref】 如果School是一个接口类型实现的多态的实现类,那么如果要修改传入的对象的类型就很简单,直接ref引用不同的类型即可

IOC技术实现了解耦合,之前创建接口和实现类;比如Dao dao = new MysqlDao();这里创建的对象和类型是紧密联系在一起的,固定的,new 出来是mysqlDao,就只能是它;用student说明就是Student和School的关系固定;而使用配置文件关系就松散一些,可以直接在ref位置修改

比如Service中需要使用dao类,然后创建Dao类,那么Service类就依赖Dao类,就可以将Dao类的赋值给拆分为声明和赋值两部分;声明的就是属性;也就是说Service类有Dao类这个属性

Spring获取对象之后底层是一个Map在维护,这里可以看以下源码

test中获取对象的方法是springcontainer.getBean(name); 可以看一下这个方法的底层

Object getBean(String name) throws BeansException;  <----BeanFactory接口的方法声明
    
public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}

这里可以看到使用的又是doGetBean的方法,可以查看一下
protected <T> T doGetBean
    Object sharedInstance = getSingleton(beanName);  --->这里就是获取了单例的对象 【Singleton 单例的对象】
    beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                         //这里传入的bean就是这里的beanInstance;
    return adaptBeanInstance(name, beanInstance, requiredType);
再看一下这个beanInstance方法
<T> T adaptBeanInstance
    return (T) bean;

//getSingleton(beanName)  获取对象
Object singletonObject = this.singletonObjects.get(beanName);
这里看到是通过get方法获取到的对象,一般get方法都是Mapprivate final Map<String, Object> singletonObjects

发现这里确实是一个Map类型的,所以在Spring内部就是Map实现一一的对象的对应,和Tomcat中的前一张Map类似

引用类型自动注入

Spring根据某些规则,可以对引用类型进行赋值,简单类型还是只能手动赋值。 不再需要使用ref使用手工注入

使用最常用的自动注入式byName和byType;使用自动注入之后,就不需要大量的管理依赖注入了

< property name = " " ref = “” />

……

byName自动注入

按名称自动注入: java类中引用类型的属性名和spring容器【配置文件】中< bean>的id名称一致,且数据类型是相同的,这样的容器中的bean,容器会自动赋值给引用类型

要使用自动注入,需要在标签中制定autowire

<bean id="mystudent" class="cfeng.entity.Student" autowire="byName">
        <property name="stuno" value="1"/>
        <property name="stuname" value="李四"/>
        <property name="stuclass" value="HC2001"/>
        <property name="stuqq" value="1378789"/>
    ……  引用类型不需要写出来了
  </bean>

这样通过反射机制,spring自动在配置文件中寻找与属性名一致的、并且数据类型一致的bean,然后通过ref赋值给这个对象,这就是依赖的自动注入

byType自动注入

按照类型自动注入,java类型中引用的数据类型和spring容器中的bean的class属性是 同源【一类】关系,这样的bean就可以赋值给引用类型,只是比较数据类型有些局限

  • java类的引用数据类型和bean的class值是相同的
  • java类的引用类型和bean的class属性值是父子关系 【子可以赋值给父类型】
  • java类的应用类型和bean中的class属性值是接口和实现类的关系 【 实现类赋值给接口类型】

指定autoWire为byType : 但是这个没有byName精确 【id才是唯一的】Map的Set

<bean id="mystudent" class="cfeng.entity.Student" autowire="byType">
        <property name="stuno" value="1"/>
        <property name="stuname" value="李四"/>
        <property name="stuclass" value="HC2001"/>
        <property name="stuqq" value="1378789"/>
    </bean>

这里就会自动获取类的属性,然后执行bean识别出要进行属性的注入,依次执行,发现有引用类型,然后得到起数据类型为School,然后就从配置文件中找寻class为该类型的或者子类型,或者找实现类,找到就放入。

但是这种匹配方式,一旦匹配到多个,比如既有本身又有子类,那么就会报错没有唯一bean的异常;所以自动注入最好使用byName – byType中,只能有一个符合条件的,如果有多个就是错误的

多配置文件

当项目的规模比较小,比如二三十个类的时候,一个配置文件是完全够用的,但是当类的数量很多的时候,用一个配置文件可能造成文件很大,打开变慢,并且多人的协作开发就变得很困难;这个时候就可以使用多配置文件

  • 每一个文件的大小比一个文件要小很多,效率高
  • 避免多人竞争带来的冲突 【 项目中有多个模块(学生考勤模块、学生成绩模块)】 每一个模块使用一个配置文件

多文件的分配方式

按照功能分 : 一个模块一个配置文件

按照类的功能 : 数据库的相关的类一个配置文件,做事务功能的一个配置文件,做service功能的一个配置文件……

包含关系的主配置文件

当有多个配置文件的时候,如果简单的进行分离,出现的问题就是,不同的配置文件不能进行引用,因为引用类型的注入依赖的就是在本配置文件中查找; 那么就需要声明一个主配置文件,用来包含各个模块的配置文件

比如之前的学校和学生是两个功能模块,那么就设置两个配置文件

//school-context.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="someService" class="cfeng.service.impl.SomeService"/>
    <bean id="mydate" class="java.util.Date"/>
    <bean id="school" class="cfeng.entity.School">
        <property name="address" value="北京"/>
        <property name="buildTime" value="1998"/>
    </bean>
</beans>

第二个模块就是学生模块

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="mystudent" class="cfeng.entity.Student" autowire="byType">
        <property name="stuno" value="1"/>
        <property name="stuname" value="李四"/>
        <property name="stuclass" value="HC2001"/>
        <property name="stuqq" value="1378789"/>
    </bean>
</beans>

但是本来学生的bean是依赖school的bean的,这样分开是无法正常依赖的,执行会报错

NullPointerException: Cannot invoke “cfeng.entity.School.setAddress(String)” because the return value of “cfeng.entity.Student.getSchool()” is null

所以要将各个模块的配置文件关联起来,这里就需要使用一个综合的包含的配置文件

spring-total: 主配置文件,包含其他的配置文件,这个文件一般是不包含任何对象的

使用标签import 标签的resource 属性就可以指定, 关键字就是类路径classpath — 在spring主配置文件中,要指定其他文件的path,就需要使用classpath,和mybatis中是相同的target/classes(主配置文件指定mapper文件 也是多个)使用classpath: 就可以指定路径是class path下面去寻找 默认也是

//这里的配置文件都直接放在的resources下面,所以直接就是文件名

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

   <import resource="classpath:school-context.xml"/>
    <import resource="classpath:student-context.xml"/>
</beans>

这样最后要调用配置文件的时候,就直接调用整个主配置文件即可

 String config = "spring-total.xml";
 ApplicationContext springContainer = new ClassPathXmlApplicationContext(config);
 Student student = (Student) springContainer.getBean("mystudent");
 student.getSchool().setAddress("beij");
 System.out.println(student);

这里就不会报异常了,和之前的结果就是相同的,各模块的依赖关系就建立了【相当于将所有的小容器的内容放入一个大的容器;这样就变为一个容器了,依赖关系就是正常的了】

除了可以一个一个import之外,还可以使用*通配符, *表示任意字符

<import resource="cfeng/spring-*.xml" />

这样就相当于将所有的spring- 开头的xml文件都导入到这个包含关系的spring-total文件中;但是spring-total.xml文件就会自己导入自己 —造成无限死循环了 所以, 主配置文件不能包含在通配符的范围内 ,同时要使用通配符,那么所有的匹配的文件应该放在目录中,不能直接放在resource下面 , 这里就是因为resource下面会放很多的其他的文件,所以避免Spring导入了其他的框架或者项目的文件; 源码中就会判断,如果没有目录,就不会进行通配符的操作,要手动import

spring的配置文件也可以使用${}的方式,但是感觉没有很大的必要

使用的方式和mybatis的格式一样;首先定义properties文件,其中放入name和value;mybatis是再environments标签上面加入properties标签

然后再spring的配置文件中加入这个文件;标签是property-placeholder

<context:property-placeholder location=""/>

然后再property标签中就可以使用${name}

因为mybatis的数据库的标签也是property,所以实质上两者都是相似的,property定义的都可以使用${}引入外部的配置文件

注意这些框架的路径都是classpath ; 在mybatis中不用强调,在spring中要用classpath:来指明路径的格式

基于注解的DI

之前的DI的实现都是依靠的是配置文件xml;但是那样子感觉非常麻烦,因为大量的类的时候就会非常的繁琐;对于DI使用注解,将不再需要在Spring配置文件中声明bean实例

  1. 首先要在项目中加入Spring的依赖,这里回间接加入spring-aop依赖,注解的使用必须使用aop

  2. 在类中加入spring的注释【有很多不同的注解】

  3. Spring中使用注解,要在原有的环境上做出改变, 需要在原来的配置文件中配置扫描器, 来在指定的包中扫描注释

常用的Spring注解有@Component @Repository @Service @Controller @Value @AutoWired @Resource;【在tomcat中常用的注解就是@webservlet @webListenser @Webfilter ……大概吧,好久没用过了】

组件扫描器

那么要识别这个注解,需要在配置文件中使用组件扫描器

<?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">

    <!-- 加入组件扫描器组件
    组件就是java对象的 base-package指定注解所在的包
    工作方式 : 扫描器会扫描base-package的包以及子包中所有的类,识别其中的注解,按照功能创建对象或者为属性赋值
    -->
    <context:component-scan base-package="cfeng.entity"/>
</beans>

这里只用指定到包就可以了,为了最大的效率,需要指定具体一点,不要扫描没有注解的类,效率低

加入组件扫描器,会自动加上新的约束文件:spring-context.xsd; 并且给这个约束文件一个命名空间的名称;

https://www.springframework.org/schema/context/spring-context.xsd这个约束文件名太长,命名空间就相当于其别名,也就是http://www.springframework.org/schema/context context: 这个前缀代表的就是另外一个约束文件,因为Spring很庞大,有很多约束文件,也就有很多命名空间,之前的时候,没有扫描器,只有一个命名空间,就是xsi,这样就不需要前缀,但是现在有多个,为了区分,就要加上前缀,所以扫描器标签

<context:component-scan base-package="cfeng.entity"/>

前面就有前缀context

指定多个包

扫描器不可能只是扫描一个包,会扫描多个包,那么如何扫描多个包呢?

  1. 多次使用扫描器标签 compnent-scan
<context:component-scan base-package=""/>
<context:component-scan base-package=""/>
<context:component-scan base-package=""/>
……
  1. 使用分隔符,分号;或者逗号,都可以
<context:component-scan base-package="cfeng.entiry;cfeng.dao;cfeng.controller;cfeng.util;……"/>
  1. 指定父包
<context:component-scan base-package="cfeng"/>   会扫描很久,所以不建议层级太高,扫描效率不高

@Component 创建对象

(component 组件)这里还是使用之前的Student类,在类中使用注解@Component;这个注解的作用就是创建对象; 就相当于xml文件中的bean标签,其有属性为value,就相当于bean的id标签;值是唯一的【真的和servlet相同,servlet的注解@webservlet也是用来创建对象的,其属性也是指定url-pattern】

创建的对象就一个,Spring创建的对象都是单例的; 注解的位置就是在类的上面,表示创建这个类的对象

package cfeng.entity;

import org.springframework.stereotype.Component;

@Component(value = "myStudent")
public class Student {
    private  int stuno;
    private  String stuname;
    private  String stuclass;
    private  School school;

    public void setStuno(int stuno) {
       this.stuno = stuno;
    }

    public void setStuname(String stuname) {
        this.stunam
            ………………

这里的@Component(value = “myStudent”) 就相当于之前的

<bean id="mystudent" class="cfeng.entity.Student"/>

只使用这个注解代表调用空的构造方法; 然后注册扫描器,配置文件加入component-scan标签即可

那么接下来就测试以下对象是否创建成功,还是要创建一个容器,传入配置文件config【需要注册扫描器】

package cfeng;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;

public class testSomeService {

    @Test
    public void testSpring(){
        ApplicationContext  springcontainer = new ClassPathXmlApplicationContext("spring-total.xml");
        int count = springcontainer.getBeanDefinitionCount();
        System.out.println(count);
        String[] names = springcontainer.getBeanDefinitionNames();
        Arrays.stream(names).forEach((name) -> System.out.println(name + " : " + springcontainer.getBean(name)));
    }
}

这样就会自动扫描注解并创建对象

5
myStudent : Student{stuno=0, stuname='null', stuclass='null', school=null}
org.springframework.context.annotation.internalConfigurationAnnotationProcessor : org.springframework.context.annotation.ConfigurationClassPostProcessor@702b8b12
org.springframework.context.annotation.internalAutowiredAnnotationProcessor : org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@22e357dc
org.springframework.context.event.internalEventListenerProcessor : org.springframework.context.event.EventListenerMethodProcessor@49912c99
org.springframework.context.event.internalEventListenerFactory : org.springframework.context.event.DefaultEventListenerFactory@10163d6

可以看到之前写的注解的类Student已经调用了空的构造方法创建了一个空的学生; 同时,还有四个注解使用的内部的4个对象

sorry,因为网站出现问题,应该是net连接出现了问题,到开调试窗口,发现就是failed to load resource; 其实没啥大问题,重启浏览器,应该是网络阻塞了

省略value

因为component中就只有一个value属性,和servlet相同,是可以省略的 直接@component(“myStudent”)

这样是完全可行的;也是经常使用的

不指定对象的名称,spring默认的名称

也就是直接@Component; 可以试一试提供的默认的名称是什么

package cfeng.entity;

import org.springframework.stereotype.Component;

@Component
public class Student {
    private  int stuno;
    private  String stuname;
    private  String stuclass;
    private  School school;

还是执行上面的测试代码

5
student : Student{stuno=0, stuname='null', stuclass='null', school=null}
org.springframework.context.annotation.internalConfigurationAnnotationProcessor : org.springframework.context.annotation.ConfigurationClassPostProcessor@10163d6
org.springframework.context.annotation.internalAutowiredAnnotationProcessor : org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@2dde1bff
org.springframework.context.event.internalEventListenerProcessor : org.springframework.context.event.EventListenerMethodProcessor@15bbf42f
org.springframework.context.event.internalEventListenerFactory : org.springframework.context.event.DefaultEventListenerFactory@550ee7e5

可以看到,提供的默认名称就是类名全部小写

@Repository 创建dao对象

(repository 仓库) ,用在持久层类的上面,放在dao的实现类上面,表示创建dao对象,dao对象能访问数据库

@Service 创建service对象

用在业务层类的上面,表示创建业务层对象,service对象能够做业务处理,可以做事务处理

@Controller 创建controller对象

放在控制层类的上面,创建控制层对象的,控制层对象,可以接收用户提交的参数,显示请求处理的结果【不只是servlet】

repository 、service、controller这三个注解除了能够和component一样创建对象之外,还能够赋予对象特殊的含义,比如repository注解的对象都是数据持久层的对象,service注解的都是业务层的对象,controller注解的都是控制层的对象,【所以这几个注解的作用就是进行业务的分层

当一个类没有明确的角色,不是三层架构中明确的功能类,那么就是用普通的Compnent即可

@Value 简单类型赋值

上面使用组件扫描器加上Component注解就能够创建分层不明确的对象;那么要赋值还是得依靠Value注解

其属性也是vlaue,因为只有一个,其实也是可以省略的;

位置 : 属性的上方 : [建议使用] 无需set方法,相当于默认直接给属性传值 ; set方法的上方: 放在set方法之上也可以实现属性注入

在属性上方,相当于利用反射机制,获取属性赋值;所以不需要set方法,一般都采用这种方式;【只会创建一个对象】

package cfeng.entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {
    @Value(value = "1")
    private int stuno;
    @Value(value = "张三")
    private String stuname;
    @Value(value = "HC2001")
    private String stuclass;
    ……

这里的属性和配置文件时对应的,bean的属性就是value和class;property属性就是name和value; 使用注解相当于直接找到class和name;只需要注入即可也就是value

myStudent : Student{stuno=1, stuname='张三', stuclass='HC2001', school=null}

可以看到在传值成功,这里就不演示set方法了;直接通过在属性上面写就避免之前配置文件中【书写不是属性的property只要有set方法就可以执行 ------ 对应的就是在set方法上面写注解@Value】

@Autowired 引用类型属性赋值 byType

Spring框架提供的注解,实现引用类型的赋值,这个autowired使用的是autowire自动注入,默认是byType类型的;bytype使用的是三种类型,所以这里就要保证被引用的类只有一个,不能歧义。

位置 : 在属性的上面,无需set方法(推荐使用) ; 也可以直接使用在set方法上,因为很麻烦

使用注解就相当于是spring帮助将这些标签给放到配置文件,所以要想使用@Autowired,那么必须配置文件中有这个类型的对象;所以要给School加上@Compnent

Autowired注解有一个属性为required: boolean类型的,默认是true 表示引用类型赋值失败的时候,程序会报错,并终止执行

package cfeng.entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("myschoool")
public class School {
    @Value("北京")
    private  String address;
    @Value("1998")
    private  int buildTime;
………………
    
@Component("myStudent")
public class Student {
    @Value(value = "1")
    private int stuno;
    @Value(value = "张三")
    private String stuname;
    @Value(value = "HC2001")
    private String stuclass;
    @Autowired
    private School school;
    ………………

这里IDEA提示Field injection is not recommended — 就是这种属性注入不推荐【因为byType容易冲突】

myStudent : Student{stuno=1, stuname='张三', stuclass='HC2001', school=School{address='北京', buildTime=1998}}

成功进行了byType类型的引用类型的自动注入

@Autowired

qualifier — 限定符; 之前通过type自动注入相当于是更宽泛的,这里要使用byName,就需要加上限定,使用属性value可以指定对象的id【注解就相当于在配置文件的基础上再简化,工作的原理差不多,都会二次扫描,不需要担心顺序的问题】

import org.springframework.stereotype.Component;

@Component("myStudent")
public class Student {
    @Value(value = "1")
    private int stuno;
    @Value(value = "张三")
    private String stuname;
    @Value(value = "HC2001")
    private String stuclass;
    @Autowired
    @Qualifier(value = "Theschool")
    private School sthschool;
    ……………………

这里value可以省略就不多说了;最主要的是优先级问题

优先级

基于XML的DI > 基于注解的DI 【xml随时都会改动,但是注解本质是接口,需要编译,所以XML中的对象是会先进行创建的】

这里的限定器的名称和对象的id要对应;🐰之前少写了o,一直报错nosuchbean

Error creating bean with name 'myStudent': Unsatisfied dependency expressed through field 'school'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cfeng.entity.School' available

这里就发现因为报错,程序终止了,没有继续向下执行

//这里将Autowired的属性变为false ---- 这里的required是不能省略的
@Autowired(required = false)

执行程序之后

6
Theschoool : School{address='天津', buildTime=1998}
myStudent : Student{stuno=1, stuname='张三', stuclass='HC2001', school=null}

这里就没有报错,只是从结果就可以看出,注入失败,school的值为null;引用类型赋值失败,程序正常执行的,只是赋值不成功【设置为true更好,程序的问题尽早提示出来,然后将问题解决,这样就可以减少空指针异常】

将名称修改正确之后

@Component("Theschool")
public class School {
    @Value("天津")
    private  String address;
    @Value("1998")
    private  int buildTime;

Spring容器都创建了对象,所以就可以正常使用

Autowired和Qualier两注解就可以实现byName自动注入,两个注解的顺序可以任意

JDK注解@Resource 自动注入

Spring提供了对于JDK中的@Resource的支持,@Resource注解可以按照名称匹配Bean,也可以按照类型匹配Bean,默认按名称注入,JDK6版本以上支持该注解,@Resource也是既可以在属性上,也可以在方法上

也就是说,当Resource不写属性值时,首先使用byName【也就是自动查找容器中是否与该属性名相同的id的对象】, 如果没有再执行byType,找到同源的对象,所以这个注解很强大;开发中建议采用这种方式来自动注入;因为这是JDK的,不是Spring的,减少与Spring的耦合;相比@Autowired和@Qualifier要更简洁一些,可以避免byType带来的继承的不识别问题

需要注意版本的兼容问题,JDK高版本,比如博主JDK16就会兼容失败,这个时候手动导入依赖;

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

加入依赖之后可以正常使用

import javax.annotation.Resource;

@Component("myStudent")
public class Student {
    @Value(value = "1")
    private int stuno;
    @Value(value = "张三")
    private String stuname;
    @Value(value = "HC2001")
    private String stuclass;
    @Resource
    private School school;
    ……

执行的时候首先使用byName,检查容器中是否有id为属性名school的School类型的对象; 如果没有再byType,检查School同源的对象进行注入

7
Theschoool : School{address='天津', buildTime=1998}
myStudent : Student{stuno=1, stuname='张三', stuclass='HC2001', school=School{address='天津', buildTime=1998}}
org.springframework.context.annotation.internalConfigurationAnnotationProcessor : org.springframework.context.annotation.ConfigurationClassPostProcessor@21282ed8
org.springframework.context.annotation.internalAutowiredAnnotationProcessor : org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@36916eb0
org.springframework.context.annotation.internalCommonAnnotationProcessor : org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@7bab3f1a
org.springframework.context.event.internalEventListenerProcessor : org.springframework.context.event.EventListenerMethodProcessor@437da279
org.springframework.context.event.internalEventListenerFactory : org.springframework.context.event.DefaultEventListenerFactory@23c30a20

CommonAnnotationBeanPostProcessor 这个对象是引入这个之后系统新创建的支持对象

使用属性name指定id只byName注入

在开发中常常使用只想使用ByName注入,这个时候,只需要使用@Resource的name属性即可,指定id;没有就会报错; 这里不只是一个属性,所以不能省略

 @Value(value = "HC2001")
    private String stuclass;
    @Resource(name = "school")
    private School school;

如果byName自动注入失败就会直接报错

Injection of resource dependencies failed

如果成功就和上面的结果是相同的

DI两种方式的选择

  • 配置文件的DI主要是耦合度更低,不需要编译;不需要修改源代码; 但是主要是标签书写的内容太多,并且对象的值和类分开,看代码需要结合; 一般需要修改的对象就使用配置文件DI
  • 注解方式的DI特点是简单快捷,但是需要编译,使用注解看起来就很乱,侵入性,一般不需要做修改或者少的对象使用注解DI

一般是注解为主,配置文件为辅,能用注解就尽量使用注解,快捷高效

IOC能够实现业务对象之间的解耦合; 比如service和dao直接的解耦合;依赖关系变得松散一些;直接new 就固定了,而通过IOC要灵活一点;比如在配置文件中,直接换一下就可以

Spring AOP

Aop【orient 面向】 aspect-oriented programing 面向切面编程

动态代理

动态代理之前专门提过,动态代理:程序在整个运行过程中根本就不存在目标类的代理类; 目标对象的代理对象只是代理工具生成的【不是真实定义的】;比如mybatis中的dao就是动态代理的,getMapper(interface)获得的dao,并没有定义dao类实现接口; 代理对象与目标对象的代理关系在程序运行时才确定; 动态代理的实现方式有两种,一种时JDK动态代理,要求目标对象必须实现接口,通过Proxy,Menthod和InvocationHandler来处理代理

另外一种时CGLIB动态代理: 运行时扩展java类与实现java接口,时一个强大的高性能的code生成类库,Spring AOP就使用的时CGLIB;原理 : 生成目标类的子类,子类时增强过的,整个子类对象就是代理对象,使用CGLIB,要求目标类一定能够被继承,不能是final的。JDK就是实现,CGLIB的效率更高,是继承

对于JDK动态代理,就不演示代码了,之前演示过了,就先创建委托的接口和实现类;然后创建一个自己的invocationHandler类实现invocationHandler接口,接口中的invoke方法就是代理的类的功能代码;其中可以通过Method.invoke来指向对象的方法; 还可以在之前或者之后进行功能增强; 最后要使用代理类就使用Proxy的静态方法getProxyInstance就可以获得一个对象,转为接口类型就可以使用方法了;

Mybatis就是使用的JDK动态代理来生成的Dao类对象,不需要再创建Dao类

Spring AOP的底层也就是JDK动态代理和CGLIB动态代理🎄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值