余一

纸上得来终觉浅,绝知此事要躬行。

0%

重拾Spring5


title: 重拾Spring5
tag: spring
top: 80


Spring概念

Spring是一个JavaEE开源的轻量级别的框架,可以解决我们企业开发中遇到的难题,能够让编码变得更加简单,核心组件IOC容器和AOP面向切面编程。

  1. IOC控制反转:把整个创建对象的工程,交给我们的SpringIOC容器来管理,底层实现原理:反射+工厂模式实现。

  2. AOP面向切面编程:对我们功能(方法)实现前后增强,比如打印日志,事务管理,权限管理,底层是基于动态代理模式实现的。减少代码的冗余性。

Spring优势

  1. 方法的解耦,简化开发

  2. Aop技术的支持

  3. 提供声明式事务

  4. Junit单元测试

  5. 方便整合其他框架(Mybatis,SpringMVC,SpringBoot,Redis)等

Spring 与 SpringBoot的关系

SpringBoot直接采用注解化的方式启动,底层会依赖于Spring/SpringMVC注解方式启动。

总结:SpringBoot底层基于Spring/SpringMVC注解化方式实现包装。

比如:

1
2
3
4
5
6
7
1.@RestController
2.@ComponentScan("com.hluck.me")
3.@Configuration
4.@Component
5.@Scheduled
6.@Value
7.@Bean

SpringIOC底层容器原理

1.Spring模块介绍

1.Test

对应spring-test.jar. Spring提供的测试工具, 可以整合JUnit测试, 简化测试环节.

2. Spring Core

​ 核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

3.Spring-Beans

​ 这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。

外部依赖spring-core,(CGLIB)。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

4.Spring Contetxt

Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

5.Spring-Expression

模块提供了一个强大的表达式语言,用于在运行时查询和处理对象图。该语言支持设置和获取属性值;属性赋值,方法调用,访问数组的内容,收集和索引器,逻辑和算术运算,命名变量,并从Spring的IOC容器的名字对象检索,它也支持列表选择和投影以及常见的列表聚合。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

6.Spring AOP

​ 通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

7.JDBC和DAO模块(Spring DAO)

JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

8.Spring-Transaction

以前是在这里org.springframework.transaction
为JDBC、Hibernate、JDO、JPA、Beans等提供的一致的声明式和编程式事务管理支持。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

9.Spring ORM

Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

10.Spring Web MVC

​ MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>

获取bean对象

1
2
3
4
//1.读取配置文件
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.根据bean的id获取bean对象
UserEntity userEntity = classPathXmlApplicationContext.getBean("userEntity", UserEntity.class);

2.SpringIOC

  1. IOC容器非常核心的接口 BeanFactory(bean对象工厂)

  2. IOC容器基本概念:控制反转,把创建对象的过程与使用统一交给Spring来进行管理,不需要开发者自己new对象。

  3. IOC底层实现技术:反射技术 + 解析xml + 工厂模式

  4. IOC作用:降低代码的耦合度。

3.IOC的核心接口

  1. IOC的核心思想底层基于反射+工厂模式实现

  2. Spring提供IOC容器实现两种方式:

    1. BeanFactory IOC容器基本的实现,是spring内部自己使用的接口,不提供给开发者使用。

      加载配置文件过程的时候,不会创建对象,当我们在获取对象的时候才会获取创建对象。

    2. ApplicationContext BeanFactory 接口的子接口,提供更多的强大功能,适合于开发者使用。

      当我们在加载配置文件的过程中,就会将配置文件中的对象创建。

在做服务器端开发的时候,使用ApplicationContext 比较多,因为所有bean初始化操作在项目启动完成之前都已经初始化了。

ApplicationContext主要实现类

ClassPathXmlApplicationContext:对应类路径下的xml格式的配置文件

FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件

ConfigurableApplicationContext 是ApplicationContext的子接口,包含一些扩展方法

refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。所以要关闭ApplicationContext需要new此接口的对象调用close()方法。

WebApplicationContext 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。

4.SpringBean的注入方式

什么是Bean管理

  • 使用Spring创建对象
  • 使用Spring注入属性

Bean的管理有两种方式

  1. 基于XML方式配置

    1
    <bean id="userEntity" class="com.it.entity.UserEntity"></bean>

    Id:获取bean对象 唯一bean对象的名称; bean的名称不允许重复

    Class属性: 类的完整路径地址(类名称+包名称)

    默认底层使用反射技术执行无参数构造函数

  2. 基于xml方式注入属性

    DI 依赖注入: 对象的属性注入值;

    1. 基于对象属性set方法实现

      1
      2
      3
      4
      <bean id="bookEntity" class="com.mayikt.entity.BookEntity">
      <property name="bookName" value="Java"></property>
      <property name="bookPrice" value="108.00"></property>
      </bean>

    2.有参构造注入

    1
    2
    3
    4
    5
    6
    7
    8
    <bean id="orderEntity" class="com.mayikt.entity.OrderEntity">
    <!--
    <constructor-arg name="orderId" value="123456"></constructor-arg>
    <constructor-arg name="orderName" value="mayikt"></constructor-arg>
    -->
    <constructor-arg index="0" value="123456"></constructor-arg>
    <constructor-arg index="1" value="mayikt"></constructor-arg>
    </bean>

    *<constructor-arg name* 指定参数列表名称

    *<constructor-arg index 指定参数列表索引*

Spring工厂Bean

1.SpringBean的作用域

1
<bean id="userEntity" class="com.it.entity.UserEntity" scope="singleton"></bean> --单例(默认)
1
<bean id="userEntity" class="com.it.entity.UserEntity" scope="prototype"></bean>  --多例

在spring默认的情况下,bean的作用域就是为单例 节约服务器内存。

2.SpringBean的生命周期

简单分为:实例化 –> 属性赋值 –> 初始化 –> 销毁

生命周期的概念:

  1. 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
  2. 为bean的属性设置(使用反射调用set方法)
  3. 调用bean的初始化方法(需要单独在类中配置初始化的方法)
  4. 正常使用bean对象
  5. Spring容器关闭,调用该类的销毁回调方法(需要单独在类中配置销毁的方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MemberEntity {
private String name;
public MemberEntity(){
System.out.println("[第一步]-无参构造函数被执行---反射机制调用");
}

public void setName(String name) {
System.out.println("[第二步]-set方法初始化属性---反射机制调用");
this.name = name;
}

/**
* 回调调用init初始化方法
*/
public void initMethod(){
System.out.println("[第三步]-回调调用init初始化方法");
}

/**
* destroyMethod
*/
public void destroyMethod(){
System.out.println("[第五步]-回调调用destroyMethod方法");
}
}
1
2
3
4
<bean id="memberEntity" class="com.it.entity.MemberEntity" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="mayikt"></property>
</bean>
<bean id="mayiktBeanPost" class="com.it.bean.MayiktBeanPost"></bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.mayikt.entity.MemberEntity;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test07 {
public static void main(String[] args) {
ClassPathXmlApplicationContext app =
new ClassPathXmlApplicationContext("spring_07.xml");
MemberEntity memberEntity= app.getBean("memberEntity",MemberEntity.class);
System.out.println("[第四步]-获取使用到的memberEntity");
System.out.println(memberEntity);
// 手动让bean容器销毁
app.close();
}
}

3.Bean的后置处理器 作用提供更多的扩展功能 BeanPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MayiktBeanPost implements BeanPostProcessor {
/**
* 调用初始化方法之前执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在bean 初始化方法之前执行");
return bean;
}

/**
* 调用初始化方法之后执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在bean 初始化方法之后执行");
return bean;
}
}
1
<bean id="mayiktBeanPost" class="com.mayikt.bean.MayiktBeanPost"></bean>
  1. 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
  2. 为bean的属性设置 (使用反射调用set方法)
  3. 将bean传递给后置处理器 调用初始化方法之前执行
  4. 调用bean的初始化的方法(需要单独在类中配置初始化的方法)
  5. 将bean传递给后置处理器 调用初始化方法之后执行
  6. 正常使用bean对象
  7. Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)

4.配置多个 BeanPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;

public class MayiktBeanPost implements BeanPostProcessor, Ordered {
/**
* 调用初始化方法之前执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用该bean的 init方法之前");
return bean;
}

/**
* 调用初始化方法之后执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("调用该bean的 init方法之后");
return bean;
}

public int getOrder() {
return 1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;

public class MayiktBeanPost02 implements BeanPostProcessor, Ordered {
/**
* 调用初始化方法之前执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[MayiktBeanPost02:]调用该bean的 init方法之前");
return bean;
}

/**
* 调用初始化方法之后执行
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[MayiktBeanPost02:]调用该bean的 init方法之后");
return bean;
}

public int getOrder() {
return 0;
}
}
1
2
3
<!-- 后置处理器-->
<bean id="mayiktBeanPost" class="com.mayikt.bean.MayiktBeanPost"></bean>
<bean id="mayiktBeanPost02" class="com.mayikt.bean.MayiktBeanPost02"></bean>

实现Ordered接口 getOrder 值越小越优先加载.

5.混合配置

混合配置就是Java配置+ XML配置。混用的话,可以在Java配置中引入xml配置。

1
2
3
@Configuration
@ImportResource("classpath:applicationContext.xml")
public class JavaConfig{}

在Java配置中,通过@ImportResource注解可以导入一个XML配置。

使用注解注入对象

  1. @Autowired

  2. @Resources

  3. @Injected

@Autowired 是根据类型去查找,然后赋值,这就要有一个要求,这个类型只可以有一个对象,否则就会报错。

@Resources 是根据名称去查找,默认情况下,定义的变量名,就是查找的名称,当然也可以在@Resources中手动指定。

所以,如果一个类存在多个实例,那么就应该使用@Resources去注入;如果使用@Autowired,也是可以的,此时需要配合另外一个注解,@Qualifier,在@Qualifier中可以指定变量名。

条件注解

条件注解就是在满足某一个条件的情况下,生效的配置。

1.条件注解

首先在Windows中如何获取操作系统信息?Windows中查看文件夹目录的命令是dir,Linux中查看文件夹目录的命令是ls,我现在希望当系统运行在Windows上时,自动打印出Windows上的目录展示命令,linux运行时,则自动展示Linux上的目录展示命令。

首先定义一个显示文件夹目录的接口:

1
2
3
public interface ShowCmd {
String showCmd();
}

然后,分别实现Windows下的实例和 Linux下的实例:

1
2
3
4
5
6
public class WinShowCmd implements ShowCmd{
@Override
public String showCmd() {
return "dir";
}
}
1
2
3
4
5
6
public class LinuxShowCmd implements ShowCmd{
@Override
public String showCmd() {
return "ls";
}
}

接下来,定义两个条件,一个是Windows下的条件,另一个是Linux下的条件:

1
2
3
4
5
6
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().contains("windows");
}
}
1
2
3
4
5
6
7
8
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext,
AnnotatedTypeMetadata annotatedTypeMetadata) {

return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().contains("linux");
}
}

接下来,在定义Bean的时候,就可以去配置条件注解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class JavaConfig {

@Bean("showCmd")
@Conditional(WindowsCondition.class)
ShowCmd winCmd(){
return new WinShowCmd();
}

@Bean("showCmd")
@Conditional(LinuxCondition.class)
ShowCmd linuxCmd(){
return new WinShowCmd();
}
}

这里,一定要给两个Bean取一样的名字,这样在调用时,才可以自动匹配。

然后,给每一个Bean加上条件注解,当条件中的matches 方法返回true的时候,这个Bean的定义就会生效。

1
2
3
4
5
6
7
public class JavaMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
ShowCmd showCmd = (ShowCmd) context.getBean("showCmd");
System.out.println(showCmd);
}
}

条件注解有一个非常典型的使用场景,就是多环境切换。

2.多环境切换

开发中,如何在 开发/生产/测试 环境之间进行快速切换?Spring提供了Profile来解决这个问题,Profile底层就是条件注解。从这个@Profile注解的定义就可以看出来:

1
2
3
4
5
6
7
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ProfileCondition implements Condition {
ProfileCondition() {
}

public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
Iterator var4 = ((List)attrs.get("value")).iterator();

Object value;
do {
if (!var4.hasNext()) {
return false;
}

value = var4.next();
} while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));

return true;
} else {
return true;
}
}
}

我们定义一个DataSource:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class DataSource {
private String url;
private String username;
private String password;

@Override
public String toString() {
return "DataSource{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}

然后,在配置Bean时,通过@Profile注解指定不同的环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Bean("ds")
@Profile("dev")
DataSource devDataSource(){
DataSource dataSource = new DataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/dev");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}

@Bean("ds")
@Profile("prod")
DataSource prodDataSource(){
DataSource dataSource = new DataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/dev");
dataSource.setUsername("root11");
dataSource.setPassword("root111");
return dataSource;
}

最后,再加载配置类,注意,需要先设置当前环境,然后再去加载配置类:

1
2
3
4
5
6
7
8
9
10
public class JavaMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
context.getEnvironment().setActiveProfiles("dev");
context.register(JavaConfig.class);
context.refresh();
DataSource ds = (DataSource) context.getBean("ds");
System.out.println(ds);
}
}

也可以在XML文件配置,如下配置在XML文件中,必须放在其他节点后面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<beans profile="dev">
<bean class="com.ljh.redis01.entity.DataSource" id="dataSource">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/dev" />
<property name="password" value="root" />
<property name="username" value="root" />
</bean>
</beans>

<beans profile="prod">
<bean class="com.ljh.redis01.entity.DataSource" id="dataSource">
<property name="url" value="jdbc:mysql://111.111.111.111/dev" />
<property name="password" value="root111" />
<property name="username" value="root111" />
</bean>
</beans>

启动类中设置当前环境并加载配置:

1
2
3
4
5
6
7
8
9
10
public class JavaMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext context1 = new ClassPathXmlApplicationContext();
context1.getEnvironment().setActiveProfiles("prod");
context1.setConfigLocation("applicationContext.xml");
context1.refresh();
DataSource ds = (DataSource) context1.getBean("dataSource");
System.out.println(ds);
}
}

3.Aware接口

Aware接口,从字面意思上理解就是感知捕获。单纯的一个Bean是没有知觉的。

之所以UserDao可以注入到UserService中,有一个前提,就是这两个Bean都是被Spring容器管理的。如果直接new一个UserService,这是没有用的,因为UserService没有被Spring容器管理,所以也不会给它注入Bean。

在实际开发中,我们可能会遇到一些类,需要获取到容器的相关信息,那就可以通过Aware接口来实现。

Aware是一个空接口,有很多实现类:

这些实现的接口,有一些共性:

  1. 都是以Aware结尾
  2. 都继承自Aware
  3. 接口均定义了一个set方法

每一个子接口均提供了一个set方法,方法的参数就是当前Bean需要感知的内容,因此我们需要在Bean中声明相关的成员变量来接收这个参数。接收到这个参数后,就可以通过这个参数获取到容器的详细信息了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
public class AppUtil implements ApplicationContextAware {

private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

public static Object getObject(String id){
//判断容器中是否存在某个bean
boolean userDao = applicationContext.containsBean("userDao");
System.out.println(userDao);
return applicationContext.getBean(id);
}
}
  1. 在spring的配置文件中,注册方法类AppUtil,这里我们使用@Component来进行注册。之所以方法类AppUtil能够灵活自如地获取ApplicationContext,就是因为spring能够为我们自动地执行了setApplicationContext。
  2. 加载Spring配置文件时,如果Spring配置文件中所定义的Bean类实现了ApplicationContextAware 接口,那么在加载Spring配置文件时,会自动调用ApplicationContextAware 接口中的setApplicationContext方法。获得ApplicationContext对象,ApplicationContext对象是由spring注入的。
  3. 前提必须在Spring配置文件中指定该类。

AOP

Aop(Aspect Oriented Programming),面向切面编程,这是对面向对象思想的一个补充。

面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能。

概念 说明
切点 要添加代码的地方,称作切点
通知(增强) 就是向切点动态添加的代码
切面 切点 + 通知
连接点 切点的定义

1. AOP的实现

AOP基于Java的动态代理实现:

  1. cglib
  2. jdk

2. 动态代理

1.基于JKD的动态代理

  1. 定义一个计算器接口:

    1
    2
    3
    public interface MyCalculator {
    int add(int a,int b);
    }
  2. 定义计算器接口的实现:

    1
    2
    3
    4
    5
    6
    public class MyCalculatorImpl implements MyCalculator{
    @Override
    public int add(int a, int b) {
    return a + b;
    }
    }
  3. 定义代理类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class CalculatorProxy {
    public static Object getInstance(final MyCalculator myCalculator){
    return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(),
    myCalculator.getClass().getInterfaces(), new InvocationHandler() {
    /**
    * @param proxy 代理对象
    * @param method 代理的方法
    * @param args 方法的参数
    * @return
    * @throws Throwable
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println(method.getName() + "方法开始执行啦...");
    Object invoke = method.invoke(myCalculator, args);
    System.out.println(method.getName() + "方法执行结束啦...");
    return invoke;
    }
    });
    }
    }

    Proxy.newProxyInstance方法接收三个参数,第一个是一个classloader,第二个是代理多项实现的接口,第三个是代理对象方法的处理器,所有要额外添加的行为都在invoke方法中实现。