Spring的IOC和AOP

发布于 2020-11-04  594 次阅读


一,Spring

Spring框架在产生到发展已经有十几年历史了,从ssh(Spring+Struts+Hibernate)到ssm(Spring+SpringMVC+Mybatis)再到现在的SpringBoot一站式开发(基于Spring和SpringMVC),java的流行开发模式都离不开它的身影

一,Spring的发展历程

写发展历程没有什么实际应用上的作用,但一门技术产生环境和背景是需要了解的,这样可以看清它产生的目的,并有助于理解框架代码的理念,同时也能增加一下乐趣

Spring产生的时代是EJB大行其道的年代,当随着EJB的推广,伴随的问题也接踵而至,EJB体态臃肿,开发复杂,效率低,这个时候天选之子Rod Johnson发现并不是所有的现目都需要EJB这样的大型框架,

为了证明它的想法于2002年10月甚至写了一本书《 Expert One-on-One J2EE 》,介绍了当时 Java 企业应用程序开发的情况,并指出了 Java EE 和 EJB 组件框架中存在的一些主要缺陷。在这本书中,他提出了一个基于普通 Java 类和依赖注入的更简单的解决方案。在这本书发布后,一对一的 J2EE 设计和开发一炮而红。这本书免费提供的大部分基础架构代码都是高度可重用的

在书中,他展示了如何在不使用 EJB 的情况下构建高质量,可扩展的在线座位预留系统。为了构建应用程序,他编写了超过 30,000 行的基础结构代码,项目中的根包命名为 com.interface21,所以人们最初称这套开源框架为 interface21,也就是 Spring 的前身,

2003 年 Rod Johnson 和同伴在此框架的基础上开发了一个全新的框架命名为 Spring ,据 Rod Johnson 介绍 Spring 是传统 J2EE 新的开始。随后 Spring 发展进入快车道。

2004年Spring发布,并一路迭代

二,Spring的框架架构

Spring其实指得是Spring framework

一,Spring的下载和包结构

一,Spring的下载

Spring下载地址:https://repo.spring.io/libs-release-local/

进入之后URL之后:org -> springframework -> spring ->选择版本 

二,Spring的包结构

Spring安装包的文件存放:

doce:存放spring说明性文档和API

libs:存放spring jar包

schema:spring的各种组件的的xsd文件

 readme.txt、notice.txt、license.txt:说明性文档。

libs文件下的jar包分类:

.jar:  spring 的class文件jar包

-source.jar:spring的源文件压缩包包(eclipse导入这个文件可以查看源码)

-javadoc.jar:spring的api压缩包

二,Spring架构

spring与其说是框架它更像是一个容器,这个容器可以容纳其他各种框架

Spring采取的是分层架构,你可以调用不同层的其他框架,极大的降低的开发的复杂性,它的核心是IOC(控制反转)和AOP(切面编程)

Sping的分层架构大概包含了20多个模块分别为:

1,Core Container:核心容器 它包含四个组件

Bean:负责Bean工厂中的Bean的装配(把配置文件和注解的信息)

Core:负责IOC的基本实现

Context:负责Bean与Bean关系的维护,是容器中Bean关系的集合,当出现多个Bean注入和依赖时会大量调用Core,它们之间的关系就由Context维护

SpEl(Spring Expression Language):Spring表达式语言

2,AOP:面向切面编程

3,Aspects:切面(和AOP相辅相成)

4,Instrumentation(边缘化):设备检测,提供对JVM和tomcat的其他的检测

5,messaging(边缘化)消息处理

6, Data Access/Integration:数据访问和集成,它主要分五类

JDBC:对JDBC的封装

ORM:对象关系映射,主要是集成其他框架,如Mybatis

OXM:java对象和xml的转换

JMS:生产者和消费者的消息功能的实现

Transactions:事务的管理

7,WEB:前端展示

WebSocket:提供Socket通信,web端的的推送功能;

Servlet:Spring MVC框架的实现;

Web:包含web应用开发用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类,Struts集成类、文件上传的支持类、Filter类和大量辅助工具类;

Portlet:实现web模块功能的聚合(如网站首页(Port)下面可能会有不同的子窗口(Portlet))

不同模块的jar包依赖:

1,IOC功能实现需要jar:

spring-core-5.0.0.RELEASE.jar

spring-context-5.0.0.RELEASE.jar

spring-bean-5.0.0.RELEASE.jar

spring-expression-5.0.0.RELEASE.jar

2,AOP功能实现需要的jar包依赖:

spring-aop-5.0.0.RELEASE-sources.jar:切面编程,bean代理

spring-aspects-5.0.0.RELEASE-sources.jar:切面

org.aspectj:aspectjweaver:支持切入点表达式

org.aspectjrt:aspectjrt:主持AOP相关注解

cglib:无接口代理

 

三,Spring的IOC,DI,AOP简述

一,IOC的原理

IOC的本质:工厂容器

IOC的行为作用:控制反转

1,主动获取对象和被动获取对象

主动获取:直接通过new获取对象

被动获取对象:不直接new获取对象,通过中间工厂类获取对象

控制反转:被动获取对象,从原来的主动new获取对象到从String的Bean工厂中获取对象

2,IOC的实现过程

IOC主要用到的技术:工厂模式,XML解析,反射

IOC的底层实现过程其实挺简单:

第一步:配置Bea工厂配置文件

第二步:通过XML解析配置文件(也可以注解)

第三步:反射创建Bean的实体类

3,手动实现简单的IOC

我们不用XML解析,用java自带的Properties文件解析

例:有两个类FQ和WQL,FQ里面有一个LOVE方法,WQL得到工厂对象调用LOVE方法,工厂读取外部文件

工厂接口:

public interface FactoryCreat {

public FQWQL creat(final String classname);
}

FQ类接口:

public interface FQWQL {

}

工厂实例类:

public class FQFactory implements FactoryCreat{
//读取的配置文件路径
String configpath;

//读取配置文件的类
Properties properties=new Properties();

public FQFactory(String configpath) {
// TODO Auto-generated constructor stub

this.configpath=configpath;
}
//要创建的类名
public FQWQL creat(final String classname) {
// TODO Auto-generated method stub
FQWQL fq=null;
try {
//读取配置文件
InputStream in=new FileInputStream(new File(configpath));

//加载配置文件
properties.load(in);

//通过键获取配置文件的类路径
String Classpath=properties.getProperty("ClassPath");

//获取配置文件的类名
String ClassName = properties.getProperty("ClassName");

//判断class名是否一致
if(classname.equals(ClassName)) {
//创建对象
Class<?> a=Class.forName(Classpath);

fq=(FQWQL) a.newInstance();

}else {
System.out.print(classname+"错误!");

}

} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.print("文件不存在!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return fq; 

}}

FQ实例类:

public class FQ implements FQWQL{

public void LOVE(){
System.out.print("WQL love FQ");
}
}

WQL类:

public class WQL {

public void FQ() {
FQFactory fqfactory=new FQFactory("fq.properties");
FQ a=(IOC.FQ) fqfactory.creat("FQ");//调用FQ的LOVE方法
a.LOVE();
} 
}

测试类:

public static void main(String[] args) {

WQL a=new WQL();
a.FQ();
}
}

输出结果:

WQL love FQ

二,DI的原理

DI:依赖注入

依赖是设计模式中的概念,它主要指类和类的关系(聚合和组合关系),指得是一个类内部包含了另外一个类,通过反射在运行时把类的依赖动态的注入

IOC和DI的关系:IOC是DI的基础,没有IOC也就没有DI,DI需要对象的创建,IOC负责生成Bean工厂生成对象,在对象的生成过程中注入对象的依赖

DI底层通过Set方法和恶有参构造实现依赖注入

传统的依赖的解决:

依赖类:

public class test1 {
test2 test;
test2 tests = new test2();//组和直接实现
public test1(test2 test) {
this.test=test; //通过构造方法实现依赖(聚合)
}
public void setTest(test2 test) {
this.test = test;//通过set方法实现依赖(聚合)
}

}

被依赖类:

public class test2 {

public void wql() {
System.out.print("被依赖!!000");
}
}

 

DI依赖注入:

依赖类:

@Configuration//标明这是配置类
@ComponentScan(basePackages = "com.wql*")//注解扫描
@Component("id")//生成Bean对象
public class test1 {
    @Autowired//按类型注入,底层自动生成set方法,按类型直接注入
    test2 test;
   public  void  fq(){
       test.v();
   }
}

被依赖类:

@Component
public class test2 {
    public void v(){
        System.out.print("DI注入成功!!");

    }
    @Override
    public String toString() {
        return "test2{DI:依赖注入}";
    }
}

测试类:

public class mainwql {
    public static void main(String[] args) {
        ApplicationContext factoryBean =  new AnnotationConfigApplicationContext(test1.class);
        test1 a=(test1) factoryBean.getBean("id");
        a.fq();
    }
}

结果:

DI注入成功!!

三,AOP的原理

AOP本质是OOP语言的一种发展分支,是OOP设计模式的一种实际应用

AOP:面向切面编程,这个切面实际指的是方法增强这种行为,

原因:OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为(日志、安全、事务)的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,模块间的藕合度高,而不利于各个模块的重用,而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性

AOP是底层是一种cjlib动态代理,通过增强方法对原有方法进行增强

AOP的几个重要名词:

1,连接点:可以被增强的方法

2,切入点:已经被增强的方法

3,通知:被增强的逻辑部分

通知的多种分类:

前置通知(@Before):在增强方法在被代理的实际方法之前执行
后置通知(@After):在实际方法之后执行
环绕通知(@Around):在实际方法前后都执行
异常通知(@AfterThrowing):在实际方法出现异常之后执行
最终通知(@AfterReturning):最终通知

4,切面:是一种动作,方法被增强的过程

自定义通过代理实现AOP的功能:去我之前写的代理模式看,我不写了

四,IOC和DI的操作

一,IOC的类架构

BeanFactory是Bean的根接口

Beanfactory的API:

Bean有众多的接口和实现类,它们在不同应用场景中使用

我们主要把常用的类来解释:

我们一般用BeanFactory和ApplicationContext来生成Bean工厂

BeanFactory和ApplicationContext的区别:

1,BeanFactory在调用时才初始化工厂生成对象

2,ApplicationContext在声明时就完成了初始化(一般用ApplicationContext生成对象)

我们生成对象时经常用到了类:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext

她们都是ApplicationContext接口下的实现类,

ClassPathXmlApplicationContext:在当前类的包下找xml配置文件

FileSystemXmlApplicationContext:在系统路径下找xml配置文件

二,IOC的操作和细节

一,Bean工厂的配置

通过bean标签生成一个bean工厂的实际对象

属性:

id:这个类对象的唯一标识

class:class的路径

例:

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--bean的创建-->
    <bean id="fq" class="com.springwql.FQLOVE">
    
    </bean>
</beans>

FQLOVE类:

package com.springwql;
public class FQLOVE {
    String a;
    WQLLOVE wql;
    public  FQLOVE(){
    }
    public  FQLOVE(String a){

        this.a=a;
    }
    public void setA(String a) {
        this.a = a;
    }
    public  void setWql(WQLLOVE wql){
        this.wql=wql;

    }
    public  WQLLOVE getWql(){
        return wql;
    }
      public void add(){
        System.out.print(a);
    }
}

测试类中通过工厂Bean调用FQLOVE对象:

@Test
public void fq(){
    //bean创建
    BeanFactory factory=new ClassPathXmlApplicationContext("wql.xml");
    FQLOVE a=(FQLOVE) factory.getBean("fq");
    a.add();
}

结果:

null

二,Bean的作用域

作用域:指得是单例还是多例,单例说明每次生成的是同一个对象,多例每一次是不同的对象

默认在同一个Bean中生成的对象都是单例的,但可以通过scope属性去控制

scope属性值:

singleton:表示单例

prototype:表示多例

 

我们来测试是否为单例或者多例:

xml配置:

<bean id="fq" class="com.springwql.FQLOVE" scope="singleton">
    <property name="a" value="作用域:单例"></property>
</bean>

<!--多例-->
<bean  id="fq1" class="com.springwql.FQLOVE" scope="prototype">
    <property name="a" value="作用域:多例"></property>
</bean >

测试:

@Test
public  void fq6(){
    BeanFactory factory=new ClassPathXmlApplicationContext("wql6.xml");
    //这两是单例创建出来的
    FQLOVE a=(FQLOVE) factory.getBean("fq");
    FQLOVE b=(FQLOVE) factory.getBean("fq");
    //看是否相等
    System.out.println(a==b);
    //多例创建出来的
    FQLOVE a1=(FQLOVE) factory.getBean("fq1");
    FQLOVE b1=(FQLOVE) factory.getBean("fq1");
    System.out.println(a1==b1);
}

结果:

true
false

三,DI依赖注入

一,通过有参注入

有参注入是DI的重要方式之一,

通过constructor标签实现:

name:参数名称

value:参数赋的值(基本类型的值)

ref:引用类型的值(外部bean)

注意:原方法中必须要有有参的方法

例:

<bean id="fq" class="com.springwql.FQLOVE">
    <constructor-arg name="a" value="傅晴"/>
</bean>

二,通过set方法注入

set注入也是DI的重要方式,总共也就两种

通过property标签实现set注入:

name:参数名称

value:要赋的值,基本类型

ref:引用类型,外部bean

注意:配置文件set注入原方法必须要有set方法,注解set注入就不用,因为它会自动生成set

例:

<bean id="fq1" class="com.springwql.FQLOVE">
 <property name="a" value="love傅晴"></property>
</bean>

三,P名称空间方式注入

名称空间需要在xml文件头部假如P名称空间

头部添加:

xmlns:p="http://www.springframework.org/schema/p

格式:p:名称 = 值

例:

<bean id="fq2" class="com.springwql.FQLOVE" p:a="傅晴loveWQL"></bean>

四,外部bean和内bean的注入

1,外部bean的注入

通过property和constructor中的ref引入外部bean

格式:ref ="外部bean的id"

例:

<bean id="fq1" class="com.springwql.FQLOVE">
   <!-- <property name="wql" ref="fq"></property>-->
   <property name="wql" >
       <!--注入空值-->
    <null></null>
   </property>
</bean>
<!--创建被注入外部bean对象-->
<bean id="fq" class="com.springwql.WQLLOVE">
<constructor-arg name="a" value="哈哈"/>
</bean>
2,内部bean的注入

property和constructor标签内部直接建bean对象

例:

<bean id="fq2" class="com.springwql.FQLOVE">
    <property name="wql">
        <bean id="cd" class="com.springwql.WQLLOVE">
            <constructor-arg name="a" value="注入内部bean对象"></constructor-arg>
        </bean>
    </property>
</bean>
3,级联赋值

级联赋值底层依赖get方法,通过引入对象,简单的赋值

步骤:

1,在实体类中set和get方法

2,在级联bean中通过set注入或者有参构造引入bean对象

3,在级联bean中通过对象name点属性名赋值

例:

<!--级联赋值-->
<bean id="fq3" class="com.springwql.FQLOVE">
    <!--必须引入对象-->
    <property name="wql" ref="fq55"></property>
    <!--级联赋值必须在调用对象的方法中声明get方法,因为他要引用这个对象,还必须要在对象方法中设置set方法,因为他要赋值-->
    <property name="wql.a" value="级联赋值必须创建对象的get方法"/>
</bean>
<!--引入级联对象-->
<bean id="fq55" class="com.springwql.WQLLOVE">
</bean>

五,集合的注入

一,set,list,array的注入

列表类集合都可以通过list,Array,set标签完成赋值

内部标签:

<value>:基本类型赋值

<ref>:引用类型赋值

格式:

<Array>

<value>值一</value>

<value>值……</value>

</Array>

例:

<!--注入集合-->
<!--数组的注入(Array,set和数组是一样的)-->
<bean id="fq" class="com.springwql.WQLcollection">
    <property name="a">
        <array>
        <value>love</value>
        <value>傅晴</value>
        </array>

    </property>
</bean>
二,Map集合的注入

map类集合都可以通过 map标签完成赋值

内部标签:<entry ></entry>

entry的属性:

key:键

value:值

ref:对象引用

格式:

<map>

<entry key="键1" value="值1"></entry>

<entry key="键……" value="值……"></entry>

</map>

例:

<!--Map的注入-->
<bean id="fq1" class="com.springwql.WQLcollection">
    <property name="arr">
        <map>
            <entry key="1" value="FQ"/>
            <entry key="2" value="WQL"/>
        </map>
    </property>
</bean>
三,公共集合

当一个集合被多个对象使用时可以用把集合抽取成公共的,共享使用

公共集合依赖util命名空间,我们需要引用命名空间:

xmlns:util="http://www.springframework.org/schema/util
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd

格式:<util : 集合类型 属性>

<集合类型标签>

</util>

集合类型:

1,array:数组

2,set:set集合

3,map:map集合

4,null:空集

5,list:列表

属性:

id:集合的唯一标识

scope:单例还是多例前面有

例:

<!--建立一个公共的集合(需要引入util)-->
<util:set id="wql11" >
    <ref bean="fq1"></ref>
    
</util:set>
<!--引入公共的集合类-->
<bean id="fq100" class="com.springwql.wqlduix">
    <property name="set" ref="wql11"></property>
</bean>

Bean的生命周期

正常情况下Bean的生命有五个:

1,通过有参构造方法创建Bean对象

2,对属性进行注入赋值(set注入)

3,调用bean初始化方法(需要配置)

init-method=方法名 ,指定初始化方法

4,获取Bean对象

5,销毁Bean后执行的方法(需要配置)

destroy-method=方法名 ,指定销毁方法

代码演示:

配置文件:

<?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="fq" class="com.springwql.lifecyclewql" init-method="initwql" destroy-method="destroywql">

        <property name="name" value="WQL love FQ"></property>

    </bean>
</beans>

实体类:

public class lifecyclewql {
    String name;

    public lifecyclewql(){

        System.out.println("1,构造创建bean");
    }

    public void setName(String name) {
        System.out.println("2,属性注入赋值(set)");
        this.name = name;
    }

    public void initwql(){//初始化方法

        System.out.println("3,初始化方法开始执行");
    }

    public void destroywql(){//销毁方法
        System.out.println("5,bean销毁");
    }

    @Override
    public String toString() {
        return "lifecyclewql{" +
                "name='" + name + '\'' +
                '}';
    }
}

测试:

@Test
public void f(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("wql11.xml");
   lifecyclewql a= (lifecyclewql)applicationContext.getBean("fq");
   System.out.println("4,获取bean对象");
    System.out.print(a);
    applicationContext.close();//close在接口类中没有,要么直接本类,要么强转


}

输出结果:

1,构造创建bean
2,属性注入赋值(set)
3,初始化方法开始执行
4,获取bean对象
lifecyclewql{name='WQL love FQ'}
5,bean销毁

后置处理

加了后置处理器后,Bean的生命周期就成了七步了

后置处理器的生命周期有七步:

1,通过有参构造方法创建Bean对象

2,对属性进行注入赋值(set注入)

3,将bean实例传入后置处理器

4,调用bean初始化方法(需要配置)

5,将bean对象传入后置处理器

6,获取Bean对象

7,销毁Bean后执行的方法(需要配置)

后置处理器:在xml文件中如果有后置处理器,那么在xml文件任何一个bean对象的调用中,初始化前后都会加上后置处理器的逻辑

后置处理器的配置:需要实现BeanPostProcessor接口实现,并实现postProcessBeforeInitialization和postProcessAfterInitialization方法

postProcessBeforeInitialization方法:在初始化之前执行

postProcessAfterInitialization方法:在初始化之后执行

按先前代码演示

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="fq" class="com.springwql.lifecyclewql" init-method="initwql" destroy-method="destroywql">

        <property name="name" value="WQL love FQ"></property>

    </bean>
    <!--如果继承了BeanPostProcessor,后被自动作为后置处理器-->
    <bean id="fq1" class="com.springwql.houzclqiwql"></bean>
</beans>

后置处理器类:

public class houzclqiwql implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("3,bean传入后置处理器");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("5,bean传入后置处理器");
        return bean;
    }
}

测试类:

@Test
public void f(){
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("wql11.xml");
   lifecyclewql a= (lifecyclewql)applicationContext.getBean("fq");
   System.out.println("4,获取bean对象");
    System.out.print(a);
    applicationContext.close();//close在接口类中没有,要么直接本类,要么强转


}

结果:

1,构造创建bean
2,属性注入赋值(set)
3,bean传入后置处理器
4,初始化方法开始执行
5,bean传入后置处理器
6,获取bean对象
lifecyclewql{name='WQL love FQ'}
7,bean销毁

 

六,bean注解配置

一,bean管理的注解

针对bean管理的注解:在class上面使用

1,@Component:通用的,

属性:value:其他bean的id值,锁定名称

2,@Service:用在server,业务逻辑层上

3,@Controller:用在web层上

4,@Repository:用在dao层上
这四种注解是作用都是一样的,只是针对的场景

属性:

value:相当于bean中的id

二,针对依赖注入的注解

基于注解实现注入:
1,@Autowired:根据属性类型自动装配

2,@Qualifier:根据属性名自动装配(它和Autowired一起使用)

属性:value:其他bean的id值,锁定名称

3,@Resource:可以根据类型也可以根据名称装配

4,@Value:属性基本类型注入

属性:value,注入基础类型的值

三,注解xml配置

引入context空间:

xmlns:context="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd

开启注解的主件扫描:

<context:component-scan base-package="com.springwql"></context:component-scan>

base-package:包路径

四,注解的使用

注入类:

@Component(value = "a")//生成一个bean对象,在进行注入
public class zhujewql2 {
   
    @Autowired
    @Qualifier(value = "q")
    zhujewql a;

    public void i(){
        a.f();
    }

被注入类:

@Component(value = "q") //value相当于xml配置文件中的ID,如果没有value默认是类名首字母小写
public class zhujewql {
 
     String wql="wql love fq";

     public  void f(){
         System.out.print(wql);
     }
}

七,完全注解开发

通过注解取代xml配置文件

@Configuration:表示不依赖配置文件,通过注解配置

@ComponentScan:注解的扫描

注意使用完全注解开发:就不能使用用来加载配置文件的方法获取bean了

需要通过AnnotationConfigApplicationContext来加载注解中的信息

三,AOP的操作

一,注解方式

关键注解:@Aspect:生成一个代理对象

1,切点表达式

切点表达式:execution([访问修饰符] [返回值] [类全路径].[方法名](参数))

注:默认public修饰符用*表示即可,void返回值也可以用*表示,无参数有...表示

例:execution(* com.gz.dao.BookDao.add(…))

2,xml的配置

配置1:引入context,开启注解扫描

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

配置2:引入aop,开启代理对象

xmlns:aop="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

例:

<?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:aop="http://www.springframework.org/schema/aop"
       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/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--Aop xml配置:配置context和aop-->
    <!--注解组件扫描-->
    <context:component-scan base-package="com.springwql"></context:component-scan>
    <!--开启aspects生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

3,通知类型

  • 前置通知(@Before):在我们执行目标方法之前运行。
  • 后置通知(@After ):在我们目标方法运行结束之后,不管有没有异常。
  • 返回通知(@AfterReturnning):在我们目标方法正常返回运行。
  • 异常通知(@AfterThrowing) :在我们目标方法出现异常后运行。
  • 环绕异常(@Around):动态代理,需要手动执行JoinPoint.procced()其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知。

4,注解AOP案例

代理类:

@Component
@Aspect //表示它是一个增强类
public class proxywql2 {

@Before("execution(* com.springwql.proxywql.wql())")
public void wql1(){
System.out.print("代理");

}
}

被代理类:

@Component(value = "wql10")
public class proxywql {

    public void wql(){
        System.out.print("哈哈哈哈,傅晴");

    }
}

测试方法:

@Test
public void fq10() {
    BeanFactory factory = new ClassPathXmlApplicationContext("wql10.xml");
    proxywql a = (proxywql) factory.getBean("wql10");
    a.wql();

}
}

结果:

代理哈哈哈哈,傅晴

路漫漫其修远兮,吾将上下而求索