Spring AOP-- 构造注入&代理模式&AOP
依赖注入;(掌握)
什么是依赖注入?(DI),是Spring里面的特性
依赖注入就是一个类里面依赖于另外一个类,然后Spring把类的对象创建出来,然后再注入到相应的类里面,这个过程就叫做依赖注入。
依赖注入的方式:
1. Set方法注入
2. 构造器注入
3. 注解方式注入
要使用依赖注入就要先配置环境导包:
//导包
<?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>cn.itsource</groupId>
<artifactId>spring-day2</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/test/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
property:属性注入的意思
//普通方式注入 相当于就是调用了Set直接赋值
<property name = "id" value="1"> </property> //property:属性注入的意思
//数组方式注入
<property name = "arrays" >
<array>
<value>'数组方式注入'</value>
<value>'数组方式注入'</value>
<value>'数组方式注入'</value>
</array>
</property>
//List注入
<property name = "list" >
<list>
<value>'数组方式注入'</value>
<value>'数组方式注入'</value>
<value>'数组方式注入'</value>
</list>
</property>
//List 存放对象 引用对象
<property name = "otherBeanList" >
<list>
<bean class="cn.itsource._01_di.OtherBean">'list存放对象'</bean>
<bean class="cn.itsource._01_di.OtherBean">'list存放对象'</bean>
<bean class="cn.itsource._01_di.OtherBean">'list存放对象'</bean>
//注意: 不引用外部Bean对象是不相同的
<ref bean="otherBean"> '引用外部bean' </ref>
<ref bean="otherBean"> '引用外部bean' </ref>
//注意: 引用外部bean是一样的对象
//set 和list的写法是一样的
</list>
</property>
//Map注入
<property name = "map" >
<map>
<entry key="key1" value="迪迪">'map方式注入'</entry>
<entry key="key2" value="强强">'map方式注入'</entry>
<entry key="key3" value="yyk">'map方式注入'</entry>
</map>
</property>
// properties 方式注入 掌握
private properties p1;
private properties p2;
// properties 方式一
<property name = "p1" >
<props>
<entry key="key1"> "方式一value值写在这个位置,支持中文"</entry>
<entry key="key2" >"方式一value值写在这个位置,支持中文"</entry>
<entry key="key3" >"方式一value值写在这个位置,支持中文"/entry>
</props>
</property>
// properties文件是键值对方式的
//作比较
//properties方式二
<property name = "p2" >
<props>
<prop key="key1" ></prop>
<prop key="key2" ></prop>
<prop key="key3" ></prop>
</props>
</property>
<property name = "p2" >
<value>
key1="方式二是这样注入值 且不支持中文版"
key2="方式二是这样注入值 且不支持中文版"
key3="方式二是这样注入值 且不支持中文版"
</value>
</property>
XML自动注入;(了解)
使用XML自动注入,可以简化我们XML的配置。
不过对于学习阶段,并不建议大家这样来使用。
简化spring的配置文件
byName 按照属性的名称 setXxx方法
bean.id的名称
byType 按照注入对象的类型,要求:类型只能配置一个实例
setXxx方法 注入类的类型(Class)
和配置文件里面的类型进行比较
配置文件里面的类型只能是1个
配置方案
根节点beans default-autowire=“byName” 对当前配置文件的所有bean都生效
子节点bean autowire="byType"只对当前bean生效
<bean id="userDao" class="cn.itsource._02_.UserDaoImpl"></bean> <bean id="userService" class="cn.itsource._02_.UserServiceImpl"></bean> <bean id="userServicex" class="cn.itsource._02_.UserServiceImpl"></bean> <bean id="userController" class="cn.itsource._02_.UserController" autowire="byName"></bean>
default-autowire="byName" //通过名字注入
default-autowire="ByType" //通过类型注入
全注解注入配置;(重点掌握)
1.1. 配置context命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
//第一步:配置让Spring扫描类与支持注解
/*
@Component 普通仓库
@Repository 仓库(其实就是dao层)
@Service service层
@Controller 控制层(servlet、action)
*/
//进行包的扫描,去看类上面是否有相应的标签配置
<context:component-scan base-package="cn.itsource._03_anno" />
//这个不是必须的(spring3.2版本前使用) 配上后兼容性好
<context:annotation-config />
@Scope(“prototype”)//默认产生单例 这是配置多例
注解配置bean的名称
//接口有两个实现(可能性比较少)
完成之后,大家注意,现在的配置还有一个问题,比如说我按真实情况来一个Dao接口,然后这个接口有两个实现,现在就会出问题
IUserDao (UserJdbcDaoImpl/UserJpaDaoImpl)
而我们声明的时候就是使用了IUserDao
@Service
public class UserService {
@Autowired
private IUserDao userDao;
public void save(){
userDao.save();
}
}
//此时会就出错:因为Spring不知道你用的是哪一个dao的实现
//解决的方案:为dao生成的bean加上名称,我们在调用的时候确定名称即可。
@Repository("userJdbcDao")
public class UserJdbcDaoImpl implements IUserDao{
public void save(){
System.out.println("保存数据xxxx....");
}
}
@Repository("userJpaDao")
public class UserJpaDaoImpl implements IUserDao{
public void save(){
System.out.println("保存数据xxxx....");
}
}
//调用名称两套方案:
//方案一:使用@Autowired
@Service
public class UserService {
@Autowired
@Qualifier("userJdbcDao")
private IUserDao userDao;
public void save(){
userDao.save();
}
}
//方案二:使用@Resource
@Service
public class UserService {
@Resource(name="userJpaDao")
private IUserDao userDao;
public void save(){
userDao.save();
}
}
那么,使用哪一种方法更好呢? 使用AutoWired;
AutoWired和Resource有什么区别? 面试题
byName 根据名字 byType 根据类型
- autowired 和 resource 都可以注入对象;
- autowired是Spring里面提供的注解,resource是java原生支持的注解
- autowired可以和Spring进行无缝的衔接(效率更好)
- autowired默认根据类型进行配置,如果匹配不是,就根据name进行匹配
- resourc是先根据名字匹配,匹配不上再根据类型进行匹配
- 建议使用AutoWired这个注解
AOP;(掌握)
AOP他也是Spring一个特性,面向切面的编程
什么叫面向切面编程?(AOP)
他的产生并不是取代面向对象,而是对面向对象进行扩展,可以按照一定的方式,把类切开(切到方法上面),可以往方法前后添加一些逻辑
什么叫面向对象编程? (OOP)
用一个实实在在在的个体去操作数据,我们在写程序,可以把上面内容都看成对象,使用对象思想进行编程
AOP使用场景
事务管理 日志管理 性能监控 拦截器
AOP实现
低耦合/松耦合: 类和类之间的链接程度很低
底层是通过代理模式实现 — 代理模式 JDK/CGLIB动态代理
JDK:针对有接口情况下使用的代理模式 – 优先推从
CGLIB:没有接口的情况下使用的代理模式 — 不能定义为 final修饰类
Xml版实现AOP
// 配置 添加aop命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
//前置增强(通知)
<aop:config>
<!--pointcut 切点 (* cn.itsource.aop.I*Service.*(..)) 代表所有下面的指定的包下面所有的 I开头的所有的* Service 接口上面方法前面 -->
<aop:pointcut expression="execution(* cn.itsource.aop.I*Service.*(..))" id="pointcut" />
<!-- asoect 方面或者切面 -->
<aop:aspect ref="txManager">
<!-- 前置通知 -->
<aop:before method="begin" pointcut-ref="pointcut"/>
<!-- 后置通知 -->
<aop:after-returning method="commit" pointcut-ref="pointcut"/>
<!-- 异常通知 -->
<aop:after-throwing method="rollback" throwing="e" pointcut-ref="pointcut"/>
<!-- 最终通知 -->
<aop:after method="close" pointcut-ref="pointcut" />
</aop:aspect>
<bean id="txManager" calss=" cn.…………………………"></bean>
</aop:config>
//=========================================分割线==========================================
//环绕增强(前面的4个增强就不用了)
<!--
环绕通知:有了环绕,就不需要用另外几个通知(会重复)
如果有两个以上的通知,建议使用环绕
-->
<aop:around method="around" pointcut-ref="pointcut" />
//环绕通知需要写一个环绕方法
import org.aspectj.lang.ProceedingJoinPoint;
public Object around(ProceedingJoinPoint joinPoint){
Object object = null;
try {
begin();
object = joinPoint.proceed(); //执行相应的代码
commit();
} catch (Throwable e) {
rollback(e);
}finally{
close();
}
return object;
}
注解版本实现AOP
//配置文件
<!-- 开启扫描 -->
<context:component-scan base-package="cn.itsource.包名" />
<!-- 支持aop注解支持 -->
<aop:aspectj-autoproxy />
//在类上面配置这两个注解
@Component //让Soring扫描到该类
@Aspect //AOP的增强类注解 把该类的功能添加到业务上去
//然后再方法上面配置
//设置切点
@Pointcut("execution(* cn.itsource.包名.I*Service.*(..))")
public void pointcut(){}
//再配置前置通知 这样可控性比较低 最好还是使用环绕通知
//前置通知
@Before("pointcut()") //@Before 前置通知注解
public void begin(){
System.out.println("开启事务....");
}
//======================================分割线======================================
//环绕通知方法:
//温馨提醒:如果要使用环绕通知的话,把其它几个通知的注解去掉(不然会出现重复)
@Around("pointcut()") //环绕通知注解 Around
public Object around(ProceedingJoinPoint joinPoint){
Object object = null;
try {
begin();
object = joinPoint.proceed(); //执行相应的代码
commit();
} catch (Throwable e) {
rollback(e);
}finally{
close();
}
return object;
}
代理模式;(掌握,超难)
单例模式
工厂模式
装饰者模式
适配器模式
代理模式: proxy
代理模式分类:
静态代理模式: 缺点 如果对多个接口或者多个方法,写很多额外的代理实现
动态代理模式 — JDK(有接口的代理)/CGLIB(没有接口的代理)
开发专业术语:
抽象主题角色:
代理主题角色:
真实主题角色: