AOP面向切面编程

面向对象的编程思想是从上往下,但是面向切面编程的时候就是横向的,思考如下:

创建出对象,里面有a对象的方法,也有b对象的方法,横向抽取两个对象的方法,然后存如到代理对象,就像bean在构造好之后,并没有立即使用,而是经过一步步的增强,BeanDefinitionPostProcessor,BeanPostProcessor,都会增强bean,往里面注入bean原本没有的一些方法

实现service层方法增强

思路:

创建一个类,将增强的方法注入到service的方法,并且返回service的方法:

@Component
public class MyMethodProcessor implements BeanPostProcessor,ApplicationContextAware {

    private ApplicationContext applicationContext;

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().getPackage().getName().equals("com.liku.service.impl")) {
            Object o = Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (Object proxy, Method method, Object[] args) -> {
                        UserDao bean1 = applicationContext.getBean(UserDao.class);
                        bean1.before();
                        bean1.show();
                        Object invoke = method.invoke(bean, args);
                        bean1.after();
                        return invoke;
                    }
            );
            return o;
        }
        return bean;
    }
}

实现BeanPostProcessor,在bean初始化之前给它增强并返回,实现ApplicationContextAware接口的set方法,注入applicationContext。以上就实现了方法的增强。

AOP相关概念

目标对象:target,被增强的方法所在的对象

代理对象:proxy,对目标对象进行增强后的对象

连接点:joinPoint,目标对象中可以被增强的方法

切入点:pointCut,目标对象中实际被增强的方法

通知:advice,增强的代码逻辑

切面:aspect,增强和切入点的结合

织入:weaving,将通知和切入点结合动态组合的过程

切点表达式

导入aspectJ依赖:

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
  <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.4</version>
        </dependency>
execution([访问修饰符]返回类型 包名.类名.方法名(参数))

访问修饰符可以省略不写

返回值类型、某一级包名、类名、方法名可以使用*表示任意

包名与类名之间使用单点,表示该包下的类,使用双点..表示该包及其包下的类

参数列表可以使用两个点..表示任意参数

    <aop:config><!--将dao层的方法注入到service层,增强service层方法-->
        <!--设置切入点【目标对象service的方法】-->
        <aop:pointcut id="pointCut1" expression="execution(public void com.liku.service.impl.UserServiceImpl.show())"/>
        <aop:aspect ref="userDao">
            <!--method是增强代码类dao拥有的方法-->
            <aop:before method="before" pointcut-ref="pointCut1"/>
            <aop:after method="after" pointcut-ref="pointCut1"/>
        </aop:aspect>
    </aop:config>
    <!--增强类-->
    <bean id="userDao" class="com.liku.dao.impl.UserDaoImpl"></bean>
    <bean class="com.liku.service.impl.UserServiceImpl"></bean>
</beans>

aspect

aspectJ有五种类型:

before:在目标方法执行前执行

after-returning:目标方法执行后执行,目标方法异常时,不再执行

around:目标方法执行前后执行,有参数,参数为环绕的目标方法,目标方法异常时,环绕后方法不再执行:

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("环绕前");
    Object proceed = joinPoint.proceed();
    System.out.println("环绕后");
    return proceed;//目标方法的返回值
}

上面三个方法相当于在try里面的内容,有异常的时候不会运行,下面的方法是异常的时候也会执行

after-throwing:目标方法抛出异常的时候执行,需要异常参数throwing

after:不管是否异常,都会执行,相当于finally,最后执行

advisor

通过实现接口的方式来确定通知类型,一般很少用

GClib通过enhancer.setCallBack实现方法的增强

基于注解配置AOP

使用注解配置aop,需要开启aop自动代理,在xml文件中

<!--包扫描组件-->
<context:component-scan base-package="com.liku"/>
<!--开启aop自动代理-->
<aop:aspectj-autoproxy/>

在通知类【增强代码类】上添加对应注解,就可以实现方法增强

@Component
@Aspect
public class UserDaoImpl implements UserDao {
    @Pointcut("execution(public void com.liku.service.impl.*.*(..))")
    public void pointCut() {
        //设置切入点
    }
    @Before("UserDaoImpl.pointCut()")
    public void before() {
        System.out.println("before...");
    }

    @After("UserDaoImpl.pointCut()")
    public void after() {
        System.out.println("after...");
    }

    @Around("execution(public void com.liku.service.impl.*.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前");
        Object proceed = joinPoint.proceed();
        System.out.println("环绕后");
    }
     @AfterThrowing(pointcut = "execution(public void com.liku.service.impl.*.*(..))", throwing = "e")
    public void throwExce(Exception e) {
        System.out.println("异常抛出时运行");
    }
}

使用配置类其安全替代xml

@Configuration
@ComponentScan({"com.liku.dao","com.liku.service"})
@EnableAspectJAutoProxy
public class AspectBeanConfig {
}

事务控制

导入spring事务相关坐标:spring-tx的包、配置目标类、使用advisor标签配置切面

转账事务

A转进x钱,B转出钱,有任何一方信息修改失败就回滚事务

思考:

spring集成mybatis,配置文件添加:【spring集成mybatis有写】

<!--扫描注解包-->
<context:component-scan base-package="com.transDemo"/>
<!--配置数据源读取路径-->
<context:property-placeholder location="jdbc.properties"/>
<!--配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${user}"/>
    <property name="password" value="${pwd}"/>
</bean>
<!--配置sql实例化工厂-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--扫描mapper包-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.transDemo.mapper"/>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

导入spring有关事务的依赖:spring-tx【之前导入spring-jdbc里面含有它,这里就不导入了,官网可搜】

配置事务有关的内容:要注意实体类的形态不是单例模式

<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--配置spring提供好的有关事务的advice-->
<tx:advice id="txAdvisor" transaction-manager="transactionManager">
    <tx:attributes>
        <!--配置不同的方法事务属性、
            name表示方法名称、
            isolation表示隔离级别,解决事务并发问题、
            timeout超时时间 默认-1
            read-only表示只读,不可修改,查询操作设置为true
            propagation事务传播行为,解决事务嵌套问题
            *表示通配符-->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!--事务增强的aop-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(void com.transDemo.service.impl.*.*(..))"/>
    <aop:advisor advice-ref="txAdvisor" pointcut-ref="txPointCut"/>
</aop:config>

控制事务成功!事务的注解为:@Transactional(属性=值)

注解配置事务

上面有关事务的只留下事务管理器,然后开启事务的自动代理

<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务的自动代理-->
<tx:annotation-driven transaction-manager="transactionManager"/>

在类或者方法上加上事务注解:

@Transactional()
public void transMoney(TbAccount inAccount, TbAccount outAccount) {
    accountMapper.decMoney(outAccount.getId(), outAccount.getAccount());
    int i = 1 / 0;
    accountMapper.inreMoney(inAccount.getId(), inAccount.getAccount());
}

用全注解方式配置:

package com.transDemo.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

@Configuration
@ComponentScan({"com.transDemo.service", "com.transDemo.entity"})
@MapperScan({"com.transDemo.mapper"})
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement
public class TransConfig {
    @Bean("dataSource")
    public DataSource source(
            @Value("${driver}") String dirver,
            @Value("${url}") String url,
            @Value("${user}") String username,
            @Value("${pwd}") String password
    ) {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(dirver);
        druidDataSource.setUsername(username);
        druidDataSource.setUrl(url);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }

    @Bean("sqlsessionFactoryBean")
    public SqlSessionFactoryBean sessionFactoryBean(
            @Qualifier("dataSource") DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
    @Bean("dataSourceTransactionManager")
    public DataSourceTransactionManager dataSourceTransactionManager( @Qualifier("dataSource") DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}
posted @ 2023-02-25 18:01  Liku007  阅读(22)  评论(0编辑  收藏  举报