目录
AOP(Aspect Oriented Programming 面向切面编程)
Spring框架两大核心机制
- IoC(控制反转)/DI(依赖注入)
- AOP(面向切面编 程)
Spring概述
Spring是一个企业级的开发框架,是软件设计层面的框架,是软件设计层面的,可以将应用程序进行分层,以至于开发者可以自主选择组件:
MVC:这一层可以选择Struts2、Spring、SpringMVC框架
ORMapping:这一层 库这一层可以选择Hibernate、MyBatis、Spring Data
优点:
- 底侵入式设计
- 独立于各种应用服务器
- 依赖注入特性将组件关系透明化,降低耦合度
- 面向切面编程特性允许将通用任务进行集中式处理
- 与第三方框架良好 整合
IoC(控制反转)
如何使用IoC
- 创建Maven工程,pom.xml添加依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>aispringioc</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.11.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency> </dependencies> </project>
- 创建实体类Student
package com.xiaoming.entity;
import lombok.Data;
@Data
public class Student {
private long id;
private String name;
private int age;
}
- 传统开发方式,手动new Student
package com.xiaoming.test;
import com.xiaoming.entity.Student;
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setAge(10);
student.setId(001);
student.setName("小明");
System.out.println(student);
}
}
- 通过IoC创建对象,配置文件中添加需要管理的对象,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="student" class="com.xiaoming.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="22"></property>
</bean>
</beans>
从IoC中获取对象
package com.xiaoming.test;
import com.xiaoming.entity.Student;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
// Student student = new Student();
// student.setAge(10);
// student.setId(001);
// student.setName("小明");
//加载配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//applicationContext就相当于ioc了,从这个对象就可以取出刚刚配置的student对象了
Student student = (Student) applicationContext.getBean("student");
System.out.println(student);
}
}
配置文件
- 通过配置bean标签来完成对象的管理;
- 其中id代表对象名;
- clss是对象的模板类(所有交给IoC容器管理的类必须有无参构造函数)
- 对象成员变量通过property标签完成赋值。
- name:成员变量名。
- value:成员变量值(基本数据类型,String可以直接赋值,如果是其他引用类型,不能通过value赋值)
- ref:将IoC中的另外一个bean赋给当前成员变量(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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student" class="com.xiaoming.entity.Student"> <property name="id" value="1"></property> <property name="name" value="张三"></property> <property name="age" value="22"></property> <property name="address" ref="address"></property> </bean> <bean id="address" class="com.xiaoming.entity.Address"> <property name="id" value="1"></property> <property name="value" value="科技路"></property> </bean> </beans>
IoC底层原理
- 读取配置文件,解析XML
- 通过反射机制来实例化配置文件中所配置的所有的bean
给bean注入集合
<bean id="student" class="com.xiaoming.entity.Student">
<property name="id" value="1"></property>
<property name="name" value="张三"></property>
<property name="age" value="22"></property>
<property name="address" >
<list>
<ref bean="address1"></ref>
<ref bean="address2"></ref>
</list>
</property>
</bean>
<bean id="address1" class="com.xiaoming.entity.Address">
<property name="id" value="1"></property>
<property name="value" value="科技路"></property>
</bean>
<bean id="address2" class="com.xiaoming.entity.Address">
<property name="id" value="2"></property>
<property name="value" value="创新路"></property>
</bean>
scope的作用域
Spring管理的bean是根据scope来生成的,表示bean的作用域,共四种。默认是单例(singleton)
- singleton:单例,表示通过Spring容器(IoC)获取的bean是唯一的
- prototype:原型,表示通过Spring容器(IoC)获取的bean是不同的
- request:请求,表示在一次HTTP请求内有效
- session:回话,表示在一个用户会话内有效
request和session只适用于Web项目,大多数情况下,使用单例和原型较多。
prototype模式当业务代码获取IoC容器中的bean时,Spring才去调用无参构造创建对应bean
singleton模式无论业务代码是否获取IoC容器中的bean,Spring在加载Spring.xml时就会创建bean
Spring的继承
与Java的继承不同,Java是类层面的继承,子类可以继承父类的内部结构;
Spring是对象层面的继承,子对象可以继承父对象的属性值。 即不同的两个类实例化对象可以完成继承,前提是子对象必须包含父对象的所有属性,同时可以在这个基础上添加其他属性。
没有继承之前
在spring中加parent的继承
Spring的依赖
与继承类似,依赖也是描述bean和bean之间的一种关系,配置依赖之后,被依赖的bean一定先创建,再创建依赖的bean,如A依赖B,先创建B,再创建A。
<bean id="student" class="com.xiaoming.entity.Student" depends-on="user"></bean>
<bean id="user" class="com.xiaoming.entity.User"></bean>
Spring的p命名空间
p命名空间是对IoC/DI的简化操作,使用p命名空间可以更加方便的完成bean的配置以及bean的依赖注入
Spring的工厂方法
IoC通过工厂模式创建bean的方式有两种:
- 静态工厂模式
实例类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private long id;
private String name;
}
静态工厂方法
public class StaticCarFactory {
private static Map<Long, Car> carMap;
static {
carMap = new HashMap<Long,Car>();
carMap.put(1L,new Car(1L,"宝马"));
carMap.put(2L,new Car(2L,"奔驰 "));
}
public static Car getCar(long id){
return carMap.get(id);
}
}
配置文件spring-factory.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">
<!-- 配置静态工厂创建Car -->
<bean id="car1" class="com.xiaoming.factory.StaticCarFactory" factory-method="getCar">
<constructor-arg value="1"></constructor-arg>
</bean>
</beans>
- 实例工厂模式
实例工厂方法
package com.xiaoming.factory;
import com.xiaoming.entity.Car;
import java.util.HashMap;
import java.util.Map;
public class InstanceCarFactory {
private Map<Long, Car> carMap;
public InstanceCarFactory(){
carMap = new HashMap<Long,Car>();
carMap.put(1L,new Car(1L,"奔驰"));
carMap.put(2L,new Car(2L,"宝马"));
}
public Car getCar(long id){
return carMap.get(id);
}
}
<!-- 配置实例工厂bean -->
<bean id="carFactory" class="com.xiaoming.factory.InstanceCarFactory"></bean>
<!-- 配置实例工厂创建car-->
<bean id="car2" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="1"></constructor-arg>
</bean>
IoC自动装载(Autowrite)
IoC负责创建对象,DI负责完成对象的依赖注入,通过配置property标签的ref属性来完成,同时Spring提供了另外一种更加简便的依赖注入方式:自动装载,不需要手动配置property,IoC容器会自动选择bean完成注入。
自动装载有两种方式:
- byName:通过属性名自动装载
- byType:通过属性的数据类型自动装载
AOP(Aspect Oriented Programming 面向切面编程)
AOP是对面向对象编程的一个补充,在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面编程。将不同方法的同一个位置抽象成一个切面对象,对该切面对象进行编程就是AOP。
AOP的优点:
- 降低模块之间的耦合度
- 使系统更容易扩展
- 更好的代码复用
- 非业务代码更加集中,不分散,便于同一管理
- 业务代码更加简洁纯粹,不掺杂其他代码影响
如何使用
创建maven工程,在pom.xml添加相关代码
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
</dependencies>
例子:
定义计算器接口,实现加减乘除
package com.xiaoming.utils;
public interface Cal {
public int add(int num1,int num2);
public int sub(int num1,int num2);
public int mul(int num1,int num2);
public int div(int num1,int num2);
}
创建接口实现类
package com.xiaoming.utils.impl;
import com.xiaoming.utils.Cal;
public class CalImpl implements Cal {
@Override
public int add(int num1, int num2) {
System.out.println("add方法参数是["+num1+","+num2+"]");
int result = num1 + num2;
System.out.println("add方法的结果是"+result);
return result;
}
@Override
public int sub(int num1, int num2) {
System.out.println("sub方法参数是["+num1+","+num2+"]");
int result = num1 - num2;
System.out.println("sub方法的结果是"+result);
return result;
}
@Override
public int mul(int num1, int num2) {
System.out.println("mul方法参数是["+num1+","+num2+"]");
int result = num1 * num2;
System.out.println("mul方法的结果是"+result);
return result;
}
@Override
public int div(int num1, int num2) {
System.out.println("div方法参数是["+num1+","+num2+"]");
int result = num1/num2;
System.out.println("div方法的结果是"+result);
return result;
}
}
上述代码中,日志信息和业务逻辑的耦合性高,不利于系统的维护,使用AOP可以进行AOP进行优化,使用动态代理的方式实现。
给业务代码找个代理,打印日志信息的工作交给代理来做,这样的话业务代码只需要关注自身业务即可。
package com.xiaoming.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* 此类是帮助我们创建动态代理类的类
*/
public class MyInvocationHandler implements InvocationHandler {
//接受委托对象
private Object object = null;
//返回代理对象
public Object bind(Object object){
this.object = object;
//获取动态代理的对象
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"方法参数是["+ Arrays.toString(args) +"]");
//业务
Object result = method.invoke(this.object,args);
System.out.println(method.getName()+"方法的结果是"+result);
return result;
}
}
package com.xiaoming.test;
import com.xiaoming.utils.Cal;
import com.xiaoming.utils.MyInvocationHandler;
import com.xiaoming.utils.impl.CalImpl;
public class Test {
public static void main(String[] args) {
// Cal cal = new CalImpl();
// cal.add(1,1);
// cal.sub(2,1);
// cal.mul(2,3);
// cal.div(4,2);
CalImpl cal = new CalImpl();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Cal cal1 = (Cal)myInvocationHandler.bind(cal);
cal1.add(1,1);
cal1.sub(2,1);
cal1.mul(2,3);
cal1.div(4,2);
}
}
以上是通过动态代理实现AOP的过程,比较复杂,不好便于理解 。
Spring框架对于AOP进行封装,使Spring框架可以用面向对象的思想实现AOP。
Spring框架中不需要创建InvocationHandler,只需要创建一个切面对象,将所有的非业务代码在切面对象中完成即可,Spring框架底层会自动根据切面类即目标类生成代理对象。
LoggerAspect
package com.xiaoming.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
//Component注解相当于将该对象配置了一个bean
//Aspect注解说将其变成切面对象
@Aspect
@Component
public class LoggerAspect {
//*代表所有的方法,然后(..)代表参数
@Before(value = "execution(public int com.xiaoming.utils.impl.CalImpl.* (..))")
public void before(JoinPoint joinPoint) {
//获取⽅方法名
String name = joinPoint.getSignature().getName();
//获取参数
String args = Arrays.toString(joinPoint.getArgs());
System.out.println(name + "⽅方法的参数是:" + args);
}
@After(value = "execution(public int com.xiaoming.utils.impl.CalImpl.* (..))")
public void after(JoinPoint joinPoint) {
//获取⽅方法名
String name = joinPoint.getSignature().getName();
System.out.println(name + "⽅方法执⾏行行完毕");
}
@AfterReturning(value = "execution(public int com.xiaoming.utils.impl.CalImpl.*(..))",returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
//获取⽅方法名
String name = joinPoint.getSignature().getName();
System.out.println(name + "⽅方法的结果是" + result);
}
@AfterThrowing(value = "execution(public int com.xiaoming.utils.impl.CalImpl.*(..))",throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
//获取⽅方法名
String name = joinPoint.getSignature().getName();
System.out.println(name + "⽅方法抛出异常:" + exception);
}
}
LoggerAspect 类定义处添加的两个注解:
@Aspect :表示该类是切⾯面类。
@Component :将该类的对象注⼊入到 IoC 容器器。
具体⽅方法处添加的注解:
@Before :表示⽅方法执⾏行行的具体位置和时机。
CalImpl 也需要添加 @Component ,交给 IoC 容器器来管理理。
package com.xiaoming.utils.impl;
import com.xiaoming.utils.Cal;
import org.springframework.stereotype.Component;
@Component
public class CalImpl implements Cal {
@Override
public int add(int num1, int num2) {
int result = num1 + num2;
return result;
}
@Override
public int sub(int num1, int num2) {
int result = num1 - num2;
return result;
}
@Override
public int mul(int num1, int num2) {
int result = num1 * num2;
return result;
}
@Override
public int div(int num1, int num2) {
int result = num1/num2;
return result;
}
}
spring.xml 中配置 AOP。
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<!--自动扫面-->
<context:component-scan base-package="com.xiaoming"></context:component-scan>
<!--Aspect注解生效 为目标类自动生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
context:component-scan 将 com.xiaoming 包中的所有类进⾏行行扫描,如果该类同时添加了了
@Component ,则将该类扫描到 IoC 容器器中,即 IoC 管理理它的对象。
aop:aspectj-autoproxy 让 Spring 框架结合切⾯面类和⽬目标类⾃自动⽣生成动态代理理对象。
测试:
package com.xiaoming.test;
import com.xiaoming.utils.Cal;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2 {
public static void main(String[] args) {
//加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
//获取代理对象(因为Spring框架会自动生成代理对象,)
Cal proxy = (Cal) applicationContext.getBean("test");
proxy.add(1,1);
proxy.sub(2,1);
proxy.mul(2,2);
proxy.div(3,0);
}
}
- 切⾯面:横切关注点被模块化的抽象对象。
- 通知:切⾯面对象完成的⼯工作。
- ⽬目标:被通知的对象,即被横切的对象。
- 代理理:切⾯面、通知、⽬目标混合之后的对象。
- 连接点:通知要插⼊入业务代码的具体位置。
- 切点:AOP 通过切点定位到连接点