Spring详解-02-注解,面向切面编程

文章目录

      • 五:自动装配
      • 六:注解开发
      • 七:使用Java配置类代替XML文件
      • 八:代理模式
      • 九:Spring自带的AOP接口

SSM完整笔记

  • Mybatis-01-配置详解
  • Mybatis-02-日志,注解和分页,一对多,多对一,缓存
  • Spring详解-01-依赖注入等方式
  • Spring详解-02-注解,面向切面编程等
  • Spring详解-03-整合Mybatis,事务配置
  • SpringMVC-01-第一个SpringMVC程序,注解开发,Restful风格
  • SpringMVC-02-Ajax,拦截器,文件上传下载
  • SSM项目整合-完整流程一览
  • 笔记相关代码:https://gitee.com/chelsea_h/ssm-integration

官网文档

  • Spring文档:https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-framework-reference/core.html#spring-core
  • Mybatis文档:http://www.mybatis.org/mybatis-3/zh/index.html
  • SpringMVC文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html

五:自动装配

注:自动装配指的是引用对象的自动装配,基本类型还是需要手动设置

  1. 自动装配就是让应用程序上下文为你找出依赖项的过程。说的通俗一点,就是Spring会在上下文中自动查找,并自动给bean装配与其关联的属性!自动装配的方式有两种,xml文件方式和注解方式
  2. 手动装配
	
	
    <bean id="cat" class="com.chelsea.pojo.Cat" scope="prototype"/>
    <bean id="dog" class="com.chelsea.pojo.Dog" scope="prototype"/>

    
    
    <bean id="user" class="com.chelsea.pojo.Person" scope="prototype" p:name="chelsea" p:cat-ref="cat" p:dog-ref="dog"/>

  1. 在xml文件中进行自动装配
	
    
    
    <bean id="user1" class="com.chelsea.pojo.Person" autowire="byName">
        <property name="name" value="chelsea"/>
    bean>

    
    <bean id="user2" class="com.chelsea.pojo.Person" autowire="byType">
        <property name="name" value="chelsea"/>
    bean>
  1. 使用注解进行自动装配
    使用注解进行自动装配时,要引入命名空间并在xml文件中打开注解开关
	
    
    
    
    <context:annotation-config/>
在xml文件中开启注释后,就可以在对应的属性上使用注解进行自动装配了
	/*使用Autowired自动装配就可以不编写set方法,此注解即可以用到属性上,也可以用到set方法上*/
    /*使用Autowired不编写set方法时,前提是这个自动装配的属性在IOC容器中已经配置,且同名*/
    @Autowired
    private Dog dog;

    /*添加参数required=false,表示允许此属性值为null*/
    @Autowired(required = false)
    /*Autowired通过byName和byType的方式进行自动装配,当两种方法都不可行时,可添加Qualifier注解装配,参数为配置的bean id值*/
    //@Qualifier("cat111")
    private Cat cat;

六:注解开发

  1. 在xml文件中引入注解的命名空间
xmlns:context="http://www.springframework.org/schema/context"
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
  1. 打开注解
	
    <context:annotation-config/>
    
    <context:component-scan base-package="com.chelsea.pojo"/>
  1. 在类中使用注解

实体类如下,其他层中还有对应的@Service,@Controller,@Repository等,这几个都是将对应的类配置为Bean。

/*等价于在xml文件中配置的Bean*/
@Component
public class User {

    @Value("chelsea")
    private String name;

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

七:使用Java配置类代替XML文件

  1. 代码如下,其中@Configuration注解声明了此类为配置类,@ComponentScan定义扫描的包路径,@Import对应于xml文件中的import标签,用来整合不同的配置文件,getUser方法配置为Bean,返回对应的Bean对象。
import com.chelsea.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/*此类是spring配置类,替代了xml文件,需要添加Configuration注解*/
@Configuration
@ComponentScan("com.chelsea.pojo")
/*引入另一个配置对象*/
@Import(chelseaConfig2.class)
public class chelseaConfig {
    /*从上下文中获取bean实例的时候,需要传入方法名称,这样配置后,实体类中的注解才会生效*/
    /*就相当于之前写的一个bean标签,方法名就相当于bean标签中的id属性
    * 返回值就相当于标签的class属性,表示bean的类型*/
    @Bean
    public User getUser(){
        return new User();
    }
}
  1. 由于使用的是类配置,不能在xml文件中开启注解开关,所以在加载配置时也发生了变化,如下:
@Test
    public void testGetUser(){
		//之前的加载方式
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("userBean.xml");
        //现在的加载方式:加载注解上下文驱动
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(chelseaConfig.class);
        //从上下文中获取bean
        User user = applicationContext.getBean("getUser", User.class);
        //进行测试
        System.out.println(user.toString());
    }

八:代理模式

  1. 为什么要学习代理模式?代理模式是SpringAOP底层原理【SpringAOP和SpringMVC】,代理模式有静态代理和动态管理
  2. 代理模式角色分析
  • 抽象角色:一般使用接口或抽象类来描述
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
  • 客户:访问代理对象的人
  1. 代理模式优缺点
  • 可以使真实角色操作更加纯粹,不用去关注一些公共业务
  • 公共业务也就交给了代理角色,实现了任务的分工
  • 公共业务发生扩展的时候,方便集中管理
  • 缺点:一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低
  1. 静态代理实例(每个角色对应):实现一个房东找中介租房子的任务
// 房东群体要执行租房子的任务
public interface Rent {
    public void rent();
}
//单个房东要租出房子
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要租房子");
    }
}
//中介说,我帮你租出吧,我还会带人家看房和签合同,省得你麻烦(可以一直嵌套中介)
public class Proxy {
    // 能使用组合尽量使用组合 不使用继承
    private Host host;
    public Proxy() {
    }
    public Proxy(Host host) {
        this.host = host;
    }
    public void rent(){
        kanfang();
        host.rent();
        qianhetong();
    }
    //需要给上一层添加任务又不修改上一层时,就使用代理模式,自行添加任务
    public void kanfang(){
        System.out.println("代理带你去看房");
    }
    public void qianhetong(){
        System.out.println("中介签租赁合同");
    }
}
//客户,我来租房子啦,我直接找中介就行
public class client {
    public static void main(String[] args) {
        //房东要租房子
        Host host = new Host();
        //代理帮房东租房子,同时代理一般会有一些附属操作
        Proxy proxy = new Proxy(host);
        //你不用直接面向房东,直接找中介租房即可!
        //spring AOP 采用分层的思想,每一层就是在上一层嵌套操作而已,所以使用代理模式
        proxy.rent();
    }
}
  1. 代理模式的思想很好的服务于面向切面编程,但也有缺点
  • 一有新的业务添加就得添加完整的一层,后面层数会越来越多
  • 每一层都需要完全覆盖上一层,有好多冗余操作,参考j2ee开发的dao层和service层,就有部分冗余操作
  • 针对于这种情况,诞生了动态代理,根据反射机制自己创建需要的代理,减少了代码冗余
  1. 动态代理实例
//动态代理生成器,代理接口即可
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成的到代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    //处理代理实例 并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(target,args);
        return result;
    }
	//假如我的新代理逻辑是要添加日志功能,方法添加如下
    public void log(String msg){
        System.out.println("[debug] " + msg + "了一个用户");
    }
}
// 客户使用动态代理来租房子
public class Client {
    public static void main(String[] args) {
        //真实角色
        UserDaoImpl userDao = new UserDaoImpl();
        //代理角色 不存在
        ProxyInvocationHandler proxy = new ProxyInvocationHandler();
        //设置要代理的对象
        proxy.setTarget(userDao);
        //动态生成代理类
        UserDao daoProxy = (UserDao) proxy.getProxy();
        daoProxy.add();
    }
}

九:Spring自带的AOP接口

  1. Aop在Spring中的作用:提供声明式事务,允许用户自定义切面
  • 意为面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种方式
  • 横切关注点:跨越应用程序多个模块的方法或功能,既是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志,安全,缓存,事务等等……
  • 切片(aspect):横切关注点被模块化的特殊对象,即,它是一个类
  • 通知(Advice):切面必须要完成的工作,即,他是类中的一个方法
  • 目标(Target):被通知对象
  • 代理(Proxy):向目标对象应用通知之后创建的对象
  • 切入点(pointcut):切面通知执行”地点“的定义
  • 连接点(jointPoint):与切入点匹配的执行点
  1. Spring中AOP的使用(三种方式)——导入依赖

<dependency>  
    <groupId>org.aspectjgroupId>  
    <artifactId>aspectjweaverartifactId>  
    <version>1.9.4version>  
dependency> 
  1. 方式一:使用Spring的API接口,在切入点附近添加业务
<bean id="userService" class="com.chelsea.services.UserServicesImpl"/>

<bean id="log" class="com.chelsea.log.log"/>
<bean id="afterLog" class="com.chelsea.log.AfterLog"/>


<aop:config>
    
    <aop:pointcut id="pointcut" expression="execution(* com.chelsea.services.UserServicesImpl.*(..))"/>
    
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
  1. 方式二:自定义来实现AOP(主要是切面定义)
<bean id="diyPoint" class="com.chelsea.diy.DiyPointCut"/>

<aop:config>
    <aop:aspect ref="diyPoint">
    	
        <aop:pointcut id="pointcut" expression="execution(* com.chelsea.services.UserServicesImpl.*(..))"/>
        
        <aop:after method="after" pointcut-ref="pointcut"/>
        <aop:before method="before" pointcut-ref="pointcut"/>
    aop:aspect>
aop:config>
public class DiyPointCut {
    public void after(){
        System.out.println("方法已经执行!");
    }
    public void before(){
        System.out.println("准备执行方法!");
    }
}
  1. 方式三:注解方式

首先打开注解支持,接下来使用注解添加AOP机制

<bean class="com.chelsea.diy.AnnoPointCut" id="annoPointCut"/>


<aop:aspectj-autoproxy/>

首先直接读取此注解,然后添加响应的切入点逻辑,传入上层参数。

@Aspect
public class AnnoPointCut {

    @Before("execution(* com.chelsea.services.UserServicesImpl.*(..))")
    public void before(){
        System.out.println("============== 方法执行前!===============");
    }

    @After("execution(* com.chelsea.services.UserServicesImpl.*(..))")
    public void after(){
        System.out.println("============== 方法执行后!===============");
    }

    @Around("execution(* com.chelsea.services.UserServicesImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("环绕前");
        //获得签名
        Signature signature = joinPoint.getSignature();
        System.out.println("signature:"+signature);
        //执行方法
        Object proceed = null;
        try {
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕后");
        System.out.println(proceed);
    }
}

注:aroud注解优先级高于after等,执行结果如下

环绕前
signature:void com.chelsea.services.UserService.add()
============== 方法执行前!===============
增加了一个用户
============== 方法执行后!===============
环绕后

你可能感兴趣的:(JavaEE,spring)