spring
1. 什么是spring
spring是J2EE应用程序框架,是轻量级的IoC和AOP的容器框架,主要是针对javaBean的生命周期进行管理的轻量级容器。
2. 框架概述
-
IoC(Inversion of Control)控制反转
对象创建责任的反转,在spring中BeanFactory
是IoC容器的核心接口,负责实例化,定位,配置应用程序中的对象及建立这些对象间的依赖。
XmlBeanFactory实现BeanFactory接口,通过获取xml配置文件数据,组成应用对象及对
象间的依赖关系。简单认知就是将原本对象的创建控制权交由spring进行管理,使用者无需关注对象的创建过程;
DI 依赖注入
指 Spring 创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象。
spring的三种注入方式:
set注入 、 接口注入 、 构造方法注入 -
AOP面向切面编程
- AOP是一种编程范式,提供从还有一个角度来考虑程序结构以完好面向对象编
程(OOP)。 - AOP为开发人员提供了一种描写叙述横切关注点的机制,并可以自己主动将横
切关注点织入到面向对象的软件系统中。从而实现了横切关注点的模块化。 - AOP可以将那些与业务无关,却为业务模块所共同调用的逻辑或责任。比如
事务处理、日志管理、权限控制等。封装起来,便于降低系统的反复代码,降
低模块间的耦合度,并有利于未来的可操作性和可维护性。
- AOP是一种编程范式,提供从还有一个角度来考虑程序结构以完好面向对象编
Spring 框架的主要优点具体如下:
-
方便解耦,简化开发
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。
-
方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。
-
降低 Java EE API 的使用难度
Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。
-
方便程序的测试
Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。
-
AOP 编程的支持
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。
-
声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程。
Spring 官网列出的 Spring 的 6 个特征:
- 核心技术 :依赖注入(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换,SpEL。
- 测试 :模拟对象,TestContext框架,Spring MVC 测试,WebTestClient。
- 数据访问 :事务,DAO支持,JDBC,ORM,编组XML。
- Web支持 : Spring MVC和Spring WebFlux Web框架。
- 集成 :远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
- 语言 :Kotlin,Groovy,动态语言。
spring结构:
-
Data Access/Integration(数据访问/集成)
- JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
- ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
- OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
- JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
- Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
-
Web 模块
- Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
- Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
- Portlet 模块:提供了在 Portlet 环境中使用 MV C实现,类似 Web-Servlet 模块的功能。
- WebSocket 模块:提供对WebSocket的集成支持。
-
Core Container(核心容器)
- Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
- Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
- Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
- Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
-
Spring Aspects :提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
-
Spring AOP :提供了面向切面的编程实现。
-
Spring Test : 提供了对 JUnit 和 TestNG 测试的支持。
-
Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
IOC容器
-
BeanFactory
BeanFactory 是基础类型的 IoC 容器,它由 org.springframework.beans.factory.BeanFactory 接口定义,并提供了完整的 IoC 服务支持。简单来说,BeanFactory 就是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用它们的生命周期方法。BeanFactory 接口有多个实现类,最常见的是 org.springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配 Bean 的。
创建 BeanFactory 实例时,需要提供 Spring 所管理容器的详细配置信息,这些信息通常采用 XML 文件形式管理。其加载配置信息的代码具体如下所示:
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(“D://applicationContext.xml”));
-
ApplicationContext
ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。该接口的全路径为 org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。
ApplicationContext 接口有两个常用的实现类:
ClassPathXmlApplicationContext
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
FileSystemXmlApplicationContext该类从指定的文件系统路径中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
bean的使用方法
public class SpringTest{
@Test
public void Test(){
String xmlPath="applicationContext.xml";
String xmlPath="src/main/java/applicationContext.xml";
//获取factory工厂对象(ApplicationContext)
//以ClassPathXMLApplicationContext对象创建factory工厂
ApplicationContext applicationContext= new ClassPathXmlApplicationContext(xmlPath);
//以FileSystemXmlApplicationContext对象创建factory工厂
ApplicationContext applicationContext1=new FileSystemXmlApplicationContext(xmlPath1);
//获取对象 对象获取后为object对象,需要对其进行强制转换
applicationContext.getBean("beanId");//beanId为applicationContest.xml文件中配置的bean的id
applicationContext1.getBean("beanId");
}
}
DI
依赖注入主要有两种实现方式,分别是属性 setter 注入和构造方法注入。具体介绍如下。
-
属性 setter 注入
指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。 -
构造方法注入
指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法实现,每个参数代表一个依赖。
<?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-3.2.xsd">
<!-- 由 Spring容器创建该类的实例对象 -->
<bean id="personDao" class="com.jm.notes.PersonDaoImpl" />
<bean id="personService" class="com.jm.notes.di.PersonServiceImpl">
<!-- 将personDao实例注入personService实例中 -->
<!--setter方式注入-->
<!--<property name="personDao" ref="personDao"/>-->
<!--构造方法注入-->
<constructor-arg index="0" ref="personDao"/>
</bean>
</beans>
spring 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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
</beans>
常用的属性
属性名称 | 描述 |
---|---|
id | 是一个 Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成 |
name | Spring 容器同样可以通过此属性对容器中的 Bean 进行配置和管理,name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,使用类的全限定名 |
scope | 用于设定 Bean 实例的作用域,其属性值有 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
constructor-arg | 元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型 |
property | 元素的子元素,用于调用 Bean 实例中的 Set 方法完成属性赋值,从而完成依赖注入。该元素的 name 属性指定 Bean 实例中的相应属性名 |
ref | 和 等元素的子元索,该元素中的 bean 属性用于指定对 Bean 工厂中某个 Bean 实例的引用 |
value | 和 等元素的子元素,用于直接指定一个常量值 |
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 类型属性的依赖注入 |
map | 用于封装 Map 类型属性的依赖注入 |
entry |
bean的实例化
-
构造器实例化
<!--bean实例化对象构造器--> <bean id="personTest" class="com.jm.notes.entity.PersonTest"/> <!--id为查找实例化对象的标记,通常命名为对象名, class为对象的完全限定名-->
-
静态工厂方法实例化
/** *静态工厂类,里面包含创建对象的静态方法 */ public class StaticBaseFactory { public static PersonTest createPersonTest(){ return new PersonTest(); } }
<!--bean实例化对象静态工厂--> <bean id="personTest1" class="com.jm.notes.entity.StaticBaseFactory" factory-method="createPersonTest"/> <!--id为查找实例化对象的标记,通常命名为对象名, class为静态工厂类的完全限定名 factory-method为静态工厂类中对应实例对象的静态方法名 -->
-
实例化工厂实例化
/** *实例化工厂类,里面包含创建对象的普通方法 */ public class StaticBaseFactory { //实例化类之前会先实例化工厂 public StaticBaseFactory() { System.out.println("实例化工厂实例中"); } public PersonTest createPerson(){ return new PersonTest(); } }
<!--bean实例化对象实例工厂--> <!--配置实例工厂--> <bean id="myBaseFactory" class="com.jm.notes.entity.StaticBaseFactory"/> <!--对象实例化配置--> <bean id="personTest2" factory-bean="myBaseFactory" factory-method="createPerson"/> <!--实例化对象id为对象索引标记,factory-bean为指定的实例化工厂的id factory-method为指定实例化工厂中的对应对象的实例化方法 -->
bean 的作用域
- 作用域的种类
-
singleton(常用)
单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
-
prototype(常用)
原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例。
-
request
在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。
-
session
在一次 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。
-
global Session
在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。
-
singleton 作用域
singleton 是 Spring 容器默认的作用域,当一个 Bean 的作用域为 singleton 时,Spring 容器中只会存在一个共享的 Bean 实例,并且所有对 Bean 的请求,只要 id 与该 Bean 定义相匹配,就只会返回 Bean 的同一个实例。
在xml配置中配置对象实例(applicationContextBeanSingleton.xml)
<!--配置对象PersonTest实例化bean并指定作用域scope为Singleton-->
<bean id ="personTest" class="com.jm.notes.entity.PersonTest" scope="singleton"/>
<!--配置另一个PersonTest对象的bean不指定作用域scope-->
<bean id ="personTest1" class="com.jm.notes.entity.PersonTest"/>
测试方法
public class BeanSingletonTest{
@Test
public void SingletonTest(){
String xmlPath = "applicationContextBaseSingleton.xml";
ApplicationContext applicationContext =new ClassPathXmlApplicationContext(xmlPath);
System.out.println(applicationContext.getBean("personTest"));
System.out.println(applicationContext.getBean("personTest"));
System.out.println(applicationContext.getBean("personTest1"));
System.out.println(applicationContext.getBean("personTest1"));
}
}
输出结果
com.jm.notes.entity.PersonTest@1563da5
com.jm.notes.entity.PersonTest@1563da5
com.jm.notes.entity.PersonTest@2bbf4b8b
com.jm.notes.entity.PersonTest@2bbf4b8b
从结果上看,前两次获取的Id为personTest的对象的地址完全相同,后两次Id为personTest1的对象地址完全相同,说明申明了Singleton的bean和默认的bean都为单例模式实例化对象。
###prototype 作用域
使用 prototype 作用域的 Bean 会在每次请求该 Bean 时都会创建一个新的 Bean 实例。因此对需要保持会话状态的 Bean(如 Struts2 的 Action 类)应该使用 prototype 作用域。
在xml配置中配置对象实例
<!-配置bean实例对象PersonTest,配置scope为prototype-->
<bean id="personTest" class="com.jm.notes.entity.PersonTest" scope="prototype"/>
test class:
public class PrototypeBeanTest{
@Test
public void prototypeTest(){
String xmlPath="applicationContextBeanPrototype.xml";
ApplicationContext applicationContext =new ClassPathXmlApplicationContext(xmlPath);
System.out.println(applicationContext.getBean("personTest"));
System.out.println(applicationContext.getBean("personTest"));
System.out.println(applicationContext.getBean("personTest"));
}
}
输出结果:
com.jm.notes.entity.PersonTest@1877ab81
com.jm.notes.entity.PersonTest@305fd85d
com.jm.notes.entity.PersonTest@458c1321
每次的输出结果都不相同,说明bean scope申明为prototype的作用域下每次都创建了不同的PersonTest的实例。
###Spring生命周期
spring bean 的装配
Bean 的装配可以理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring 容器支持多种形式的 Bean 的装配方式,如基于 XML 的 Bean 装配、基于 Annotation 的 Bean 装配和自动装配等。
基于xml装配bean
基于xml的注入方式有值注入和构造方法注入两种方式,值注入需要确保值都有对应的setter方法和一个默认无参构造方法;构造方法注入需要有一个对应注入参数对应的构造方法。
创建personTest类:
package com.jm.notes.entity;
import java.util.Objects;
/**
* @描述
* @author(创建者) jm
* @date(创建时间) 2020/5/27 13:15
*/
public class PersonTest {
private String name;
private Integer age;
private String sex;
public PersonTest(String name, Integer age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public PersonTest() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "PersonTest{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
xml(applicationContextBean.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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<!--Spring bean 基于xml的装配bean-->
<!-- 使用设值注入方式装配Person实例 -->
<bean id="personTest" class="com.jm.notes.entity.PersonTest">
<property name="name" value="我是值注入测试的人"/>
<property name="age" value="12"/>
<property name="sex" value="男"/>
</bean>
<!-- 使用构造方法装配Person实例 -->
<bean id="personTest1" class="com.jm.notes.entity.PersonTest">
<constructor-arg index="0" value="我是构造方法注入测试的人"/>
<constructor-arg index="1" value="12"/>
<constructor-arg index="2" value="女"/>
</bean>
</beans>
test类:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @描述
* @author(创建者) jm
* @date(创建时间) 2020/5/27 15:10
*/
public class BeanAssemblyXmlTest {
@Test
public void assemblyXmlTest(){
String xmlPath ="applicationContextBean.xml";
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
System.out.println(applicationContext.getBean("personTest"));
System.out.println(applicationContext.getBean("personTest1"));
}
}
输出结果:
PersonTest{name='我是值注入测试的人', age=12, sex='男'}
PersonTest{name='我是构造方法注入测试的人', age=12, sex='女'}
######xml装配的当bean的数量过多时会导致xml文件变得臃肿不好维护和升级。
基于annotation(注解)装配bean
基于Annotation的常用注解:
-
@Component
用于表示一个组件bean,可以作用于任何层次上。
-
@Repository
用于数据访问层(dao层)的类标注为spring的bean。
-
@Service
通常作用域业务层(service层),用于将业务层的类标注为spring的bean。
-
@Controller
通常用于控制层(Controller层),用于将控制层的类标注为spring的bean。
-
@Autowired
用于对bean属性变量、属性的setter方法和构造函数进行标注,配合对应的注解助理bean的自动配置工作。默认按bean的类型进行装配。
-
@Resource
作用和@Autowired相同,其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。@Resource 中有两个重要属性:name 和 type。
Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。
-
@Qualifier
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。
Spring自动装配
自动装配是指Spring容器合一自动装配相互协作的Bean之间的关联关系,将Bean注入到其他的Bean的property中。
自动装配使用bean 元素中的autowire属性:
< bean id="" class="" autowire=""/>
autowire属性的值:
名称 | 说明 |
---|---|
byName | 根据 Property 的 name 自动装配,如果一个 Bean 的 name 和另一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。 |
byType | 根据 Property 的数据类型(Type)自动装配,如果一个 Bean 的数据类型兼容另一个 Bean 中 Property 的数据类型,则自动装配。 |
constructor | 根据构造方法的参数的数据类型,进行 byType 模式的自动装配。 |
no | 默认情况下,不使用自动装配,Bean 依赖必须通过 ref 元素定义。 |
default | ---- |
实例代码:
public class TestController {
private TestService testService;
public void setTestService(TestService testService) {
this.testService = testService;
}
public TestController() {
}
public TestController(TestService testService) {
this.testService = testService;
}
public void add(){
testService.add();
System.out.println("TestController add方法被调用");
}
}
public interface TestDao {
void add();
}
public class TestDaoImpl implements TestDao {
@Override
public void add() {
System.out.println("TestDao add方法被调用了-----");
}
}
public interface TestService {
void add();
}
public class TestServiceImpl implements TestService {
private TestDao testDao;
public void setTestDao(TestDao testDao) {
this.testDao = testDao;
}
public TestServiceImpl() {
}
public TestServiceImpl(TestDao testDao) {
this.testDao = testDao;
}
@Override
public void add() {
testDao.add();
System.out.println("TestService add方法被调用");
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--自动配置byName 有setter方法-->
<!--<bean id="testDao" class="com.jm.notes.annotation.TestDaoImpl"/>
<bean id="testService" class="com.jm.notes.annotation.TestServiceImpl"
autowire="byName"/>
<bean id="testController" class="com.jm.notes.annotation.TestController"
autowire="byName"/>-->
<!--byType-->
<!--<bean id="testDao" class="com.jm.notes.annotation.TestDaoImpl"/>
<bean id="testService" class="com.jm.notes.annotation.TestServiceImpl"
autowire="byType"/>
<bean id="testController" class="com.jm.notes.annotation.TestController"
autowire="byType"/>-->
<!--constructor 需要有对应的构造函数和无参构造函数-->
<bean id="testDao" class="com.jm.notes.annotation.TestDaoImpl"/>
<bean id="testService" class="com.jm.notes.annotation.TestServiceImpl"
autowire="constructor"/>
<bean id="testController" class="com.jm.notes.annotation.TestController"
autowire="constructor"/>
</beans>
test代码:
@Test
public void TestController1(){
//自动装配
ApplicationContext applicationContext1 =new ClassPathXmlApplicationContext("ApplicationContextAutowire.xml");
System.out.println(); applicationContext1.getBean("testService");
TestController testController = (TestController) applicationContext1.getBean("testController");
testController.add();
}
TestDao add方法被调用了-----
TestService add方法被调用
TestController add方法被调用
AOP(Aspect Oriented Programming) 面向切面编程
面向切面编程(AOP)和面向对象编程(OOP)类似,也是一种编程模式。Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。
AOP将业务逻辑的各个组成部分进行隔离,开发是只用专心编写核心业务,提高开发效率。
其应用主要体现在事务处理、日志管理、权限控制和异常处理等方面。
利用JDK实现AOP
/**
* @描述 切面类
* @author(创建者) jm
* @date(创建时间) 2020/5/28 11:05
*/
@Component
public class AopAspect {
public void MyBefore(){
System.out.println("method start before");
}
public void MyAfter(){
System.out.println("method end after");
}
}
public interface CustomerDao {
void add();
void update();
void delete();
void find();
}
@Repository
public class CustomerDaoImpl implements CustomerDao {
@Override
public void add() {
System.out.println("Customer add method running----");
}
@Override
public void update() {
System.out.println("Customer update method running ---");
}
@Override
public void delete() {
System.out.println("Customer delete method running ----");
}
@Override
public void find() {
System.out.println("Customer find method running ----");
}
}
/**
* @描述 代理类
* @author(创建者) jm
* @date(创建时间) 2020/5/28 11:10
*/
@Component("factory")
public class AOPBaseFactory {
//创建切面类实例
private AopAspect aopAspect;
@Autowired
public void setAopAspect(AopAspect aopAspect) {
this.aopAspect = aopAspect;
}
//目标类
@Resource
private CustomerDao customerDao;
public static CustomerDao getBean() {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContextAnnotation.xml");
final AOPBaseFactory baseFactory = (AOPBaseFactory)applicationContext.getBean("factory");
//使用代理
return (CustomerDao) Proxy.newProxyInstance(
AOPBaseFactory.class.getClassLoader(),
new Class[]{CustomerDao.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
baseFactory.aopAspect.MyBefore();
Object o = method.invoke(baseFactory.customerDao, args);
baseFactory.aopAspect.MyAfter();
return o;
}
}
);
}
}
<?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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--使用context命名空间,通知spring扫描指定目录,进行注解的解析-->
<context:component-scan base-package="com.jm.notes"/>
</beans>
test 类
public class AOPTest {
@Test
public void test() {
// 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
CustomerDao customerDao = AOPBaseFactory.getBean();
// 执行方法
customerDao.add();
customerDao.update();
customerDao.delete();
customerDao.find();
}
}
method start before
Customer add method running----
method end after
method start before
Customer update method running ---
method end after
method start before
Customer delete method running ----
method end after
method start before
Customer find method running ----
method end after
###Spring通知实现AOP
######Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口。Spring 通知按照在目标类方法的连接点位置,可以分为以下五种类型:
名称 | 说明 |
---|---|
org.springframework.aop.MethodBeforeAdvice(前置通知) | 在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。 |
org.springframework.aop.AfterReturningAdvice(后置通知) | 在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。 |
org.aopalliance.intercept.MethodInterceptor(环绕通知) | 在方法前后自动执行的通知称为环绕通知,可以应用于日志、事务管理等功能。 |
org.springframework.aop.ThrowsAdvice(异常通知) | 在方法抛出异常时自动执行的通知称为异常通知,可以应用于处理异常记录日志等功能。 |
org.springframework.aop.IntroductionInterceptor(引介通知) | 在目标类中添加一些新的方法和属性,可以应用于修改旧版本程序(增强类)。 |
确保存在aop的jar包。
proxy的基本配置属性:
- target: 代理的目标对象
- proxyInterface: 代理实现的接口,多个接口使用< list>< value>< /value>< /list>标签
- proxyTargetClass: 是否对类代理而不是接口,设置为 true 时,使用 CGLIB 代理
- interceptorNames: 需要植入目标的 Advice
- singleton: 返回的代理是否为单例,默认为 true(返回单实例)
- optimize: 当设置为 true 时,强制使用 CGLIB
实例代码:
package com.jm.notes.AOP;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @描述 切面类
* @author(创建者) jm
* @date(创建时间) 2020/5/28 11:05
*/
@Component
public class AopAspectCopy implements MethodInterceptor {
public void MyBefore(){
System.out.println("(Advice)method start before");
}
public void MyAfter(){
System.out.println("(Advice)method end after");
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕通知");
MyBefore();
// 执行目标方法
Object proceed = methodInvocation.proceed();
MyAfter();
return proceed;
}
}
class AopAspectCopy1 implements MethodBeforeAdvice {
public void MyBefore(){
System.out.println("(MethodBeforeAdvice)method start before");
}
public void MyAfter(){
System.out.println("(MethodBeforeAdvice)method end after");
}
//在方法之前自动执行的通知称为前置通知,可以应用于权限管理等功能。
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知");
MyBefore();
// 执行目标方法
//method.invoke(o,objects);
MyAfter();
}
}
class AopAspectCopy2 implements AfterReturningAdvice {
public void MyBefore(){
System.out.println("(MethodBeforeAdvice)method start before");
}
public void MyAfter(){
System.out.println("(MethodBeforeAdvice)method end after");
}
//在方法之后自动执行的通知称为后置通知,可以应用于关闭流、上传文件、删除临时文件等功能。
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("后置通知");
MyBefore();
MyAfter();
}
}
<?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-3.2.xsd">
<!--目标类 -->
<bean id="customerDao" class="com.jm.notes.AOP.CustomerDaoImpl" />
<!-- 通知 advice -->
<bean id="myAspect" class="com.jm.notes.AOP.AopAspectCopy" />
<bean id="myAspect1" class="com.jm.notes.AOP.AopAspectCopy1" />
<bean id="myAspect2" class="com.jm.notes.AOP.AopAspectCopy2" />
<!--生成代理对象 -->
<bean id="customerDaoProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!--代理实现的接口 list标签可以编写多个接口 -->
<property name="proxyInterfaces" >
<list >
<value>com.jm.notes.AOP.CustomerDao</value>
</list>
</property>
<!--代理的目标对象 -->
<property name="target" ref="customerDao"/>
<!--用通知增强目标 -->
<property name="interceptorNames" value="myAspect" />
<!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
<property name="proxyTargetClass" value="true" />
</bean>
<!--生成代理对象 -->
<bean id="customerDaoProxy1"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!--代理实现的接口 list标签可以编写多个接口 -->
<property name="proxyInterfaces" >
<list >
<value>com.jm.notes.AOP.CustomerDao</value>
</list>
</property>
<!--代理的目标对象 -->
<property name="target" ref="customerDao"/>
<!--用通知增强目标 -->
<property name="interceptorNames" value="myAspect1" />
<!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
<property name="proxyTargetClass" value="true" />
</bean>
<!--生成代理对象 -->
<bean id="customerDaoProxy2"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!--代理实现的接口 list标签可以编写多个接口 -->
<property name="proxyInterfaces" >
<list >
<value>com.jm.notes.AOP.CustomerDao</value>
</list>
</property>
<!--代理的目标对象 -->
<property name="target" ref="customerDao"/>
<!--用通知增强目标 -->
<property name="interceptorNames" value="myAspect2" />
<!-- 如何生成代理,true:使用cglib; false :使用jdk动态代理 -->
<property name="proxyTargetClass" value="true" />
</bean>
</beans>
test类
import com.jm.notes.AOP.AOPBaseFactory;
import com.jm.notes.AOP.CustomerDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @描述
* @author(创建者) jm
* @date(创建时间) 2020/5/28 11:27
*/
public class AOPTest {
@Test
public void test1() {
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationAdvice.xml");
// 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDaoProxy");
// 执行方法
customerDao.add();
customerDao.update();
customerDao.delete();
customerDao.find();
}
@Test
public void test2() {
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationAdvice.xml");
// 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDaoProxy1");
// 执行方法
customerDao.add();
customerDao.update();
customerDao.delete();
customerDao.find();
}
@Test
public void test3() {
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationAdvice.xml");
// 从工厂获得指定的内容(相当于spring获得,但此内容时代理对象)
CustomerDao customerDao = (CustomerDao) applicationContext.getBean("customerDaoProxy2");
// 执行方法
customerDao.add();
customerDao.update();
customerDao.delete();
customerDao.find();
}
}
环绕通知
(Advice)method start before
Customer add method running----
(Advice)method end after
环绕通知
(Advice)method start before
Customer update method running ---
(Advice)method end after
环绕通知
(Advice)method start before
Customer delete method running ----
(Advice)method end after
环绕通知
(Advice)method start before
Customer find method running ----
(Advice)method end after
前置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
Customer add method running----
前置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
Customer update method running ---
前置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
Customer delete method running ----
前置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
Customer find method running ----
Customer add method running----
后置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
Customer update method running ---
后置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
Customer delete method running ----
后置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
Customer find method running ----
后置通知
(MethodBeforeAdvice)method start before
(MethodBeforeAdvice)method end after
使用 AspectJ 开发 AOP 使用 Annotation方式
AspectJ 除了需要导入 Spring AOP 的 JAR 包以外,还需要导入与 AspectJ 相关的 JAR 包:spring-aspects-*;
Annotation 注解的介绍
名称 | 说明 |
---|---|
@Aspect | 用于定义一个切面。 |
@Before | 用于定义前置通知,相当于 BeforeAdvice。 |
@AfterReturning | 用于定义后置通知,相当于 AfterReturningAdvice。 |
@Around | 用于定义环绕通知,相当于MethodInterceptor。 |
@AfterThrowing | 用于定义抛出通知,相当于ThrowAdvice。 |
@After | 用于定义最终final通知,不管是否异常,该通知都会执行。 |
@DeclareParents | 用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)。 |
@RestController vs @Controller
- @Controller 返回一个页面
- @RestController 返回JSON 或 XML 形式数据
- @Controller +@ResponseBody 返回JSON 或 XML 形式数据