一:aop概念
即面向切面编程,通过预编译的方式和动态代理实现程序功能的统一维护的一种技术,是函数式编程的一种衍生泛型。通过对业务的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合程度降低,提高程序的可重用性,提高了开发的效率。
二:作用:不修改源码的情况下进行增加。
三:实现方式:动态代理
四:使用切面编程:案例的问题:就是我们平时转账的时候,会有事务的提交,如果不成功就会通过事务的回滚而达到数据库不会进行改变。如果不用spring则会很麻烦。
代码如下:
4.1 结构
4.2.工具类,作用在注入sql语句的时候,关闭自动提交事务。
/*连接的工具类,它用于从数据源中获取一个连接,并实现和线程的绑定*/
public class connectionUtils {
private ThreadLocal<Connection> t1= new ThreadLocal<Connection>();
private DataSource ds;
public void setDs(DataSource ds) {
this.ds = ds;
}
//获取当前线程上的连接
public Connection getThreadConnection(){
Connection connection = t1.get();
//判断是否有连接
if (connection==null)
{
//从数据源中获取一个连接并且存入threadLocal中
try {
connection= ds.getConnection();
t1.set(connection);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return connection;
}
//把连接从线程上解绑
public void removeThreadConnection(){
t1.remove();
}
}
4.2.2重新设置事务
public class transactionManager {
private connectionUtils connectionUtil;
public void setConnectionUtil(connectionUtils connectionUtil) {
this.connectionUtil = connectionUtil;
}
public void beginTransaction()
{
//关闭自动提交
try {
connectionUtil.getThreadConnection().setAutoCommit(false);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
public void commit()
{
try {
connectionUtil.getThreadConnection().commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
public void rollback()
{
try {
connectionUtil.getThreadConnection().rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
public void close()
{
try {
connectionUtil.getThreadConnection().close();
connectionUtil.removeThreadConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
4.3 数据库dao层
public class IaccountDaoImpl implements IaccountDao{
private QueryRunner runner;
private connectionUtils connectionUtils;
public void setConnectionUtils(utils.connectionUtils connectionUtils) {
this.connectionUtils = connectionUtils;
}
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public List<account> findAll() throws SQLException {
return runner.query(connectionUtils.getThreadConnection(),"select *from account",new BeanListHandler<account>(account.class));
}
public account findById(Integer id) throws SQLException {
return runner.query(connectionUtils.getThreadConnection(),"select *from account where id=?",new BeanHandler<account>(account.class),id);
}
public void updateAcc(account acc) throws SQLException {
runner.update(connectionUtils.getThreadConnection(),"update account set name=?,money=? where id=?",acc.getName(),acc.getMoney(),acc.getId());
}
public void deleteById(Integer id) throws SQLException {
runner.update(connectionUtils.getThreadConnection(),"delete *from account where id =?",new BeanHandler<account>(account.class),id);
}
public void insert(account acc) throws SQLException {
runner.update(connectionUtils.getThreadConnection(),"insert into account values(?,?)",acc.getName(),acc.getMoney());
}
public account findByNme(String name) throws SQLException {
List<account> accounts= runner.query(connectionUtils.getThreadConnection(),"select *from account where name=?",new BeanListHandler<account>(account.class),name);
if (accounts.size()==0||accounts==null)
{
return null;
}if (accounts.size()>1)
{
throw new RuntimeException("结果集不唯一");
}
return accounts.get(0);
}
}
4.4service层,可以清楚的感受到代码的冗余
@Component("accountService")
public class IaccountServiceImpl implements IaccountService
{
private IaccountDao accDao;
private transactionManager ts;
public void setTs(transactionManager ts) {
this.ts = ts;
}
public void setAccDao(IaccountDao accDao) {
this.accDao = accDao;
}
//还有其他方法 ...省略实现了,跟下面一样
public void insert(account acc) throws SQLException {
try {
//开启事务
ts.beginTransaction();
//执行操作
accDao.insert(acc);
//提交事务
ts.commit();
//返回结果
}catch (Exception E){
//回滚
ts.rollback();
throw new RuntimeException(E);
}finally {
// 释放
ts.close();
}
}
//模拟转账操作
public void transfer(String sourceName , String targetName ,float money) throws SQLException {
try {
//开启事务
ts.beginTransaction();
//执行操作
//获取转账人的钱数
account aa = accDao.findByNme(sourceName);
account bb = accDao.findByNme(targetName);
//转账
aa.setMoney(aa.getMoney()-money);
bb.setMoney(bb.getMoney()+money);
//更新
accDao.updateAcc(aa);
//int a=1/0;
accDao.updateAcc(bb);
//提交事务
ts.commit();
//返回结果
}catch (Exception E){
//回滚
ts.rollback();
throw new RuntimeException(E);
}finally {
// 释放
ts.close();
}
}
4.5 bean.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">
<!--配置dao-->
<!--配置service-->
<bean id="accountService" class="service.IaccountServiceImpl">
<property name="accDao" ref="accountDao"/>
<property name="ts" ref="transactionManager"></property>
</bean>
<bean id="accountDao" class="dao.IaccountDaoImpl">
<property name="runner" ref="runner1"/>
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<!--事务管理-->
<bean id="transactionManager" class="utils.transactionManager">
<property name="connectionUtil" ref="connectionUtils"></property>
</bean>
<bean id="connectionUtils" class="utils.connectionUtils">
<property name="ds" ref="dataSource"></property>
</bean>
<!-- 此处我们只注入了数据源,表明每条语句独立事务-->
<bean id="runner1" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/springOne?serverTimezone=UTC"></property>
<property name="user" value="root"></property>
<property name="password" value="admin"></property>
</bean>
</beans>
4.6 导入pom坐标
<artifactId>spring-context</artifactId>
<artifactId>spring-test</artifactId>
<artifactId>c3p0</artifactId>
<artifactId>commons-dbutils</artifactId>
<artifactId>junit</artifactId>
<artifactId>mysql-connector-java</artifactId>
4.7 测试类
public class Spring_curd1 {
@Test
public void findAll() throws SQLException {
ApplicationContext cc = new ClassPathXmlApplicationContext("bean.xml");
IaccountService accountService = cc.getBean("accountService", IaccountService.class);
accountService.transfer("aa","bb",100);
}
}
**由此可以明显的看出问题,业务层方法变得臃肿了,里面充斥着很多重复代码。并且业务层方法和事务控制方法耦合了。 试想一下,如果我们此时提交,回滚,释放资源中任何一个方法名变更,都需要修改业务层的代码。 **
五:更新后
学习 spring 中的 AOP 要明确的事 : a、开发阶段(我们做的) 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。 把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP 编程人员来做。 在配置文件中,声明切入点与通知间的关系,即切面。:AOP 编程人员来做。 b、运行阶段(Spring框架完成的) Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对 象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
5.1 结构图
5.2
不变的是:
工具类在这里是connectionUtils和transactionManager不变
dao层不变
pojo层不变
自然测试类不变
变的是因为臃肿的service而导致的变化
service
@Component("accountService")
public class IaccountServiceImpl implements IaccountService
{
private IaccountDao accDao;
public void setAccDao(IaccountDao accDao) {
this.accDao = accDao;
}
public List<account> findAll() throws SQLException {
return accDao.findAll();
}
public account findById(Integer id) throws SQLException {
return accDao.findById(id);
}
public void updateAcc(account acc) throws SQLException {
accDao.updateAcc(acc);
}
public void deleteById(Integer id) throws SQLException {
accDao.deleteById(id);
}
public void insert(account acc) throws SQLException {
accDao.insert(acc);
}
public void transfer(String sourceName, String targetName, float money) throws SQLException {
//执行操作
//获取转账人的钱数
account aa = accDao.findByNme(sourceName);
account bb = accDao.findByNme(targetName);
//转账
aa.setMoney(aa.getMoney()-money);
bb.setMoney(bb.getMoney()+money);
//更新
accDao.updateAcc(aa);
// int a=1/0;
accDao.updateAcc(bb);
}
}
bean.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:aop="http://www.springframework.org/schema/aop"
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">
<!--配置service-->
<bean id="accountService" class="lml.service.IaccountServiceImpl">
<property name="accDao" ref="accountDao"></property>
</bean>
<!--配置dao-->
<bean id="accountDao" class="lml.dao.IaccountDaoImpl">
<property name="runner" ref="runner1"></property>
<property name="connectionUtils" ref="connectionUtils"></property>
</bean>
<bean id="connectionUtils" class="config.connectionUtils">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="tsmanager" class="config.transactionManager">
<property name="connectionUtil" ref="connectionUtils"></property>
</bean>
<!-- 配置 runner此处我们只注入了数据源,表明每条语句独立事务-->
<bean id="runner1" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/springOne?serverTimezone=UTC"></property>
<property name="user" value="root"></property>
<property name="password" value="admin"></property>
</bean>
<!--配置切面编程-->
<aop:config>
<aop:aspect id="txAdvice" ref="tsmanager">
<!--前置通知-->
<aop:before method="beginTransaction" pointcut-ref="pt"></aop:before>
<!--后置-->
<aop:after-returning method="commit" pointcut-ref="pt"></aop:after-returning>
<!--异常-->
<aop:after-throwing method="rollback" pointcut-ref="pt"></aop:after-throwing>
<!--最后-->
<aop:after method="close" pointcut-ref="pt"></aop:after>
<aop:pointcut id="pt" expression="execution(* lml.service.*.*(..))"/>
</aop:aspect>
</aop:config>
</beans>
pom.xml
在上面的基础上导入aop的包
<artifactId>aopalliance</artifactId>
<artifactId>aspectjweaver</artifactId>
六:动态代理:字节码随用随创建,随用随加载。
理解:原本:a销售c。动态代理:a通过b代理,顺便b还送c礼品,流程 a-b-c;
1.基于接口(a)的增强
2.基于子类(b)的增强
1.1 基于接口(a)的增强
提供者:JDK 官方的 Proxy 类。
要求:被代理类最少实现一个接口
创建的方式
Proxy.newProxyInstance(三个参数)
* 参数含义:
ClassLoader:和被代理对象使用相同的类加载器。
Interfaces:和被代理对象具有相同的行为。实现相同的接口。
InvocationHandler:如何代理。
Object one= Proxy.newProxyInstance(
a.getClass().getClassLoader(),
a.getClass().getInterfaces(),
new InvocationHandler() {
//执行被代理对象的任何方法,都会经过该方法
// proxy:代理对象的引用。不一定每次都用得到
//method:当前执行的方法对象
// args:执行方法所需的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
增强...}
2.1基于子类(b)的增强
提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
要求:被代理类不能用 final 修饰的类(最终类)。
用到的类:
* Enhancer
* 用到的方法:
* create(Class, Callback)
* 方法的参数:
* * Class:被代理对象的字节码
* * Callback:如何代理
Object one= Enhancer.create(a.getClass(),new MethodInterceptor() {
/* 执行被代理对象的任何方法,都会经过该方法。在此方法内部就可以对被代理对象的任何 方法进行增强。
参数:
前三个和基于接口的动态代理是一样的。
MethodProxy:当前执行方法的代理对象。
*/ @Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
...}
七:
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的 连接点。
Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
Advice(通知/增强): 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Introduction(引介): 引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。
Target(目标对象): 代理的目标对象。
Weaving(织入): 是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类。 Aspect(切面): 是切入点和通知(引介)的结合。