Spring5

XML处理Bean,注解注入属性

alt

简介

Spring : 春天 --->给软件行业带来了春天

2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。

2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。

很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。

Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术

官网 : http://spring.io/

官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/

GitHub : https://github.com/spring-projects

Spring优点:

  • 是一个开源免费的框架,容器
  • 是一个轻量级的、非入侵式的框架
  • 控制反转(IOC工厂模式),面向切面编程(AOP代理模式)
  • 支持事务处理,对框架整合的支持
  • 总结:Spring就是一个轻量级的控制反转和面向切面编程的框架!

Maven导包

<!--导入spring-webmvc后,Maven会自动导入其他依赖jar包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

IOC理论推导

alt

  • 提取一个公共 UserDao 接口 , 利用set .
public class UserServiceImpl implements UserService {
   private UserDao userDao;
   // 利用set实现各种实现类的注入
   public void setUserDao(UserDao userDao) {
       this.userDao = userDao;
  }


   @Override
   public void getUser() {
       userDao.getUser();
  }
}
  • 现在去我们的测试类里 , 进行测试 ;
@Test
public void test(){
   UserServiceImpl service = new UserServiceImpl();
   service.setUserDao( new UserDaoMySqlImpl() );
   service.getUser();
   //那我们现在又想用Oracle去实现呢
   service.setUserDao( new UserDaoOracleImpl() );
   service.getUser();
}
  • 以前由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 . 程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口 .
  • 这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !
  • IOC原理:工厂模式 + 反射 ==> 降低耦合

spring.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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans                                                                    http://www.springframework.org/schema/beans/spring-beans.xsd    
                           http://www.springframework.org/schema/tx                                                                    http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/context                                                                http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop                                                                    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--引入外部属性文件:数据库配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置连接池:记得导包druid,创建dataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.username}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>

    <!--创建JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource属性值-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--1.创建事务管理器:此管理器对 jdbcTemplate 和 MyBatis有用-->
    <bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入dataSource属性值-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--2.开启基于注解的事务控制模式,依赖tx名声空间-->
    <tx:annotation-driven transaction-manager="tm"/>
    <!--3.给事务方法加注解-->

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.gwq.spring5"></context:component-scan>

    <!-- 开启Aspect生成代理对象-->
    <aop:aspectj-***></aop:aspectj-***>

    <!--基于xml配置Aop-->
    <!--第一步:导包,将目标类和切面类加入ioc容器,@Component-->
    <!--告诉spring谁是切面类,@Aspect 需要aop名称空间-->
    <!--在切面类中使用五个通知注解来配置通知方法何时何地起作用-->
    <!--开启基于配置的AOP功能-->

    <!--    <bean id="amethodProxy" class="com.gwq.spring5.AOP.AmethodProxy"></bean>
        <bean id="amethod" class="com.gwq.spring5.AOP.Amethod"></bean>

        <aop:config>
            <!-指定切面:相当于@Aspect-->
    <!--        <aop:aspect ref="amethodProxy" >
                <aop:pointcut id="pc" expression="execution(* com.gwq.spring5.AOP.Amethod.showName(..))"/>

                <aop:before method="before" pointcut-ref="pc"/>
                <aop:after-returning method="afterReturning" pointcut-ref="pc" returning="result"></aop:after-returning>
                <aop:after-throwing method="afterThrowing" pointcut-ref="pc" throwing="e"></aop:after-throwing>
                <aop:after method="after" pointcut="execution(* com.gwq.spring5.AOP.Amethod.showName(..))"></aop:after>
                <aop:around method="around" pointcut-ref="pc" ></aop:around>
            </aop:aspect>
        </aop:config>
 -->
</beans>

Bean的作用域

alt

  • 几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。

Singleton 单例模式

 <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

Prototype 原型模式

  • 创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
 <bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>  

Request

  • 当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
 <bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
  • 针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

Session

  • 当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:
 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
图片

注解开发(非常重要)

"会员" 四个注解(功能一致,随便用)

  • @Component
  • @Service ==> 业务逻辑层,service层
  • @Controller ==> web层
  • @Repository ==> dao层/Mapper

自动装配四个注解

  • @Autowired:按Type,(再按Name)
  • @Qualifier:按Name,和@Autowired一起才能使用
  • @Resource:按Name,再按Type
  • @Value(value = "..."):直接为其赋值为...
@Qualifier:根据属性名称Name,要和@Autowired一起才能使用
@Resource: 和@Autowired一样功能,是Javax自带,但功能不如@Autowired,
            但是有更大的扩展性,@Autowired离开了spring就不管用了
@Value(value = "..."):普通变量注入,会直接为name属性赋值...
private String name;

1、@Autowired:按Type查找,找不到抛异常,找到多个再按Name查找
   @Autowired(required=false) ,允许null 值

3、@Resource    按Name,再按Type,如果都找不到,抛异常

注意: 注解创建对象(接口不能new,创建实现类)

使用注解前提

  • jdk1.5开始支持注解,spring2.5开始全面支持注解。

  • 在spring配置文件中引入context文件头

    xmlns:context="http://www.springframework.org/schema/context"
    
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
  • 2、开启属性注解支持!

    <context:annotation-config/>

基于Java类进行配置(为SpringBoot打基础)

JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。

测试:

1、编写一个实体类,Dog

@Component  //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
   public String name = "dog";
}

2、新建一个config配置包,编写一个MyConfig配置类

@Configuration  //代表这是一个配置类
public class MyConfig {

   @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
   public Dog dog(){
       return new Dog();
  }

}

3、测试

@Test
public void test2(){
   ApplicationContext applicationContext =
           new AnnotationConfigApplicationContext(MyConfig.class);
   Dog dog = (Dog) applicationContext.getBean("dog");
   System.out.println(dog.name);
}

4、成功输出结果!

导入其他配置如何做呢?

1、我们再编写一个配置类!

@Configuration  //代表这是一个配置类
public class MyConfig2 {
}

2、在之前的配置类中我们来选择导入这个配置类

@Configuration
@Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {

   @Bean
   public Dog dog(){
       return new Dog();
  }

}

关于这种Java类的配置方式,我们在之后的SpringBoot 和 SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!

基于XML的Spring,有setter() (了解)

通过无参构造方法来创建

<?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="user" class="com.gwq.pojo.User">
       <property name="name" value="zhangsan"/>
   </bean>

</beans>

通过有参构造方法来创建(掌握)

  • beans.xml 有三种方式编写
    <!-- 推荐,第一种根据参数名字设置 -->
    <bean id="userT" class="com.gwq.pojo.UserT">
       <!-- name指参数名 -->
       <constructor-arg name="name" value="zhangsan"/>
    </bean>



    <!-- 不推荐,第二种根据index参数下标设置 -->
    <bean id="userT" class="com.gwq.pojo.UserT">
       <!-- index指构造方法 , 下标从0开始 -->
       <constructor-arg index="0" value="zhangsan"/>
    </bean>

    <!-- 不推荐,第三种根据参数类型设置 -->
    <bean id="userT" class="com.gwq.pojo.UserT">
       <constructor-arg type="java.lang.String" value="zhangsan"/>
    </bean>
  • 结论:单例下,在配置文件加载的时候。其中管理的对象都已经初始化了!

从容器获取Bean

//常用:     ClassPathXmlApplicationContext("...xml")        spring配置文件在类路径(src)下直接写xx.xml    
//不常用:    FileSystemXmlApplicationContext("...xml")        spring配置文件在磁盘路径下,写绝对路径

    psvm{
        ApplicationContext context = new ClassPathXmlApplicationContext("baseBean.xml");
        //在容器创建完之后 对象 也就创建完了,而不是等到getBean时再创建,系统先加载好资源效率更高
        DataSource ds1 = context.getBean("dataSource", DataSource.class);
        DataSource ds2 = context.getBean("dataSource", DataSource.class);
        //获取两次对象,默认单例:同一对象
        sout(ds1==ds2);        ==> true
    }
    ioc容器在创建对象时,property会利用setter方法为JavaBean的属性进行赋值
    property的name不由组件属性名决定,而是由setter方法名决定,所以一键生成getter、setter,这样name就是组件属性名了
        <bean id="book" class="com.xxx.Book">            <!--class写要创建的组件的全类名-->
            <property name="username" value="张三" </property>    
            <property name="password" value="123456"></property>
            <property name="a" ref="a"></property>        <!--ref="a"  引用外部Bean:a-->
        </bean>
        <!--外部bean -->    
        <bean id="a" class="com.省略.A"></bean>

Spring配置

Bean的配置

<!--bean就是java对象,由Spring创建和管理-->

<!--
       id 是bean的唯一标识符
       如果配置id,又配置了name,那么name是别名,name可以设置多个别名,可以用逗号,分号,空格隔开
       如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
       class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello1 h2,h3;h4" class="com.gwq.pojo.Hello">
   <property name="name" value="Spring"/>
</bean>

import

团队的合作通过import来实现 .

<import resource="{path}/beans.xml"/>
<!--引入外部资源-->
<context:property-placeholder location="classpath:xxx.xxx"/>

别名(没用,了解即可)

<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>

依赖注入DI

  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

1、构造器注入(已经讲过)

2、Set 注入 (重点)

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .

  • 常量注入
 <bean id="student" class="com.gwq.pojo.Student">
     <property name="name" value="小明"/>
 </bean>
  • Bean注入

注意点:这里的值是一个引用,ref

 <bean id="addr" class="com.gwq.pojo.Address">
     <property name="address" value="重庆"/>
 </bean>

 <bean id="student" class="com.gwq.pojo.Student">
     <property name="name" value="小明"/>
     <property name="address" ref="addr"/>
 </bean>
  • 数组注入
 <bean id="student" class="com.gwq.pojo.Student">
     <property name="name" value="小明"/>
     <property name="address" ref="addr"/>
     <property name="books">
         <array>
             <value>西游记</value>
             <value>红楼梦</value>
             <value>水浒传</value>
         </array>
     </property>
 </bean>
  • List注入
 <property name="hobbys">
     <list>
         <value>听歌</value>
         <value>看电影</value>
         <value>爬山</value>
     </list>
 </property>
  • Map注入
 <property name="card">
     <map>
         <entry key="中国邮政" value="456456456465456"/>
         <entry key="建设" value="145****255511"/>
     </map>
 </property>
  • set注入
 <property name="games">
     <set>
         <value>LOL</value>
         <value>BOB</value>
         <value>COC</value>
     </set>
 </property>
  • Null注入
 <property name="wife"><null/></property>
  • Properties注入
 <property name="info">
     <props>
         <prop key="学号">20190604</prop>
         <prop key="性别">男</prop>
         <prop key="姓名">小明</prop>
     </props>
 </property>

p命名和c命名注入

  • 需要在头文件中加入约束文件
 导入约束 : xmlns:p="http://www.springframework.org/schema/p"
 导入约束 : xmlns:c="http://www.springframework.org/schema/c"

 <!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
 <bean id="user" class="com.gwq.pojo.User" p:name="zhangsan" p:age="18"/>

  <!--C(构造: Constructor)命名空间 , c 就是所谓的构造器注入!属性依然要设置set方法-->
 <bean id="user" class="com.gwq.pojo.User" c:name="zhangsan" c:age="18"/>

alt

静态代理

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

alt

代码实现

Rent . java 即抽象角色

//抽象角色:租房
public interface Rent {
   public void rent();
}

Host . java 即真实角色

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
   public void rent() {
       System.out.println("房屋出租");
  }
}

Proxy . java 即代理角色

//代理角色:中介
public class Proxy implements Rent {

   private Host host;
   public Proxy() { }
   public Proxy(Host host) {
       this.host = host;
  }

   //租房,加上一些附属操作
   public void rent(){
       seeHouse();
       host.rent();
       fare();
  }
   //看房
   public void seeHouse(){
       System.out.println("带房客看房");
  }
   //收中介费
   public void fare(){
       System.out.println("收中介费");
  }
}

Client . java 即客户

//客户类,一般客户都会去找代理!
public class Client {
   public static void main(String[] args) {
       //房东要租房
       Host host = new Host();
       //中介帮助房东
       Proxy proxy = new Proxy(host);

       //你去找中介!
       proxy.rent();
  }
}

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

动态代理(代理的是接口)

  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理--cglib
    • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
    • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、

JDK的动态代理需要了解两个类

  • 核心1 : InvocationHandler :
代理实例 implement InvocationHandler接口            重写invoke(Object proxy, 方法 method, Object[] args)
每个代理实例都有一个关联的InvocationHandler 
public class InvocationHandler implements java.lang.reflect.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       //method.invoke(真实对象,参数集)执行代理过程
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //附属功能A,B
        methodA();
        //得到当前执行方法的方法名
        method.getName();
        Object result = method.invoke(target, args);
        methodB();
        return result;
    }

    //附属功能A,B
    public void methodA(){
        System.out.println("A");
    }

    public void methodB(){
        System.out.println("B");
    }
}
  • 核心2:Proxy(代理):
Proxy.newProxyInstance 参数:
    ClassLoader loder        : 
    Class<?>[] interfaces    : 代理的接口.class,  (结婚、出租房)
    InvocationHandler h        : 谁实现了InvocationHandler就传谁
    //动态得到代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance
                (this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

代码实现

  • InvocationHandler调用处理器类
public class InvocationHandler implements java.lang.reflect.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       //method.invoke(真实对象,参数集)执行代理过程
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //附属功能A,B
        methodA();
        //得到当前执行方法的方法名
        method.getName();
        Object result = method.invoke(target, args);
        methodB();
        return result;
    }

    //附属功能A,B
    public void methodA(){
        System.out.println("A");
    }

    public void methodB(){
        System.out.println("B");
    }
}
  • 代理与被代理共同目标 接口类
public interface Marry {
    public void toMarry();
}
  • 真实对象类 Man implements 接口类
public class Man implements Marry{
    public String Name;

    public Man(String name) {
        Name = name;
    }

    public Man() {
    }

    @Override
    public void toMarry() {
        System.out.println(Name+"结婚啦");
    }
}
  • 客户(结婚者)
public class Cilent {

    public static void main(String[] args) {
        //真实对象
        Man man = new Man("里尔");
        //得到调用处理器
        InvocationHandler ith = new InvocationHandler();
        //设置真实对象
        ith.setTarget(man);
        //动态获取代理对象
        Marry proxy = (Marry) ith.getProxy();
        proxy.toMarry();

    }

}

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、

AOP 代理模式(事务,日志,权限控制)

    重要功能用xml,不重要的用注解    
    原理:动态代理        ==>     降低耦合    不通过修改源代码方式,在主干功能里面添加新功能
    术语:
        1.连接点:类里面可以被增强的方法
        2.切入点:正在被增强的方法
        3.通知(增强):实现增强逻辑部分称通知(增强)
            (1)前置    @Before                原方法执行前执行
            (2)后置    @AfterReturning        return返回值之后执行,如果抛异常就不会return,就不会执行
            (3)环绕    @Around            
            (4)异常    @AfterThrowing        抛异常之后执行    
            (5)最终    @After                方法执行之后执行,一定会执行
        4.切面(动作):把通知应用到切入点的过程

        5、切入点表达式
        (1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
        (2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )

        举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
            execution(* com.atguigu.dao.BookDao.add(..))
        举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
            execution(* com.atguigu.dao.BookDao.* (..)) 
        举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
            execution(* com.atguigu.dao.*.* (..))

    //环绕通知
     @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
     public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
         System.out.println("环绕之前.........");
         //被增强的方法执行
         proceedingJoinPoint.proceed();
         System.out.println("环绕之后.........");
     }

alt

导包

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>

使用Spring实现Aop

第一种方式 : 通过 Spring API 实现

  • 首先编写我们的业务接口和实现类
public interface UserService {
   public void add();
   public void delete();
   public void update();
   public void search();
}

//====================================================
public class UserServiceImpl implements UserService{
   @Override
   public void add() {System.out.println("增加用户");}

   @Override
   public void delete() {System.out.println("删除用户");}

   @Override
   public void update() {System.out.println("更新用户");}

   @Override
   public void search() {System.out.println("查询用户");}
}
  • 然后去写我们的增强类 , 我们编写两个 , 一个前置增强 Log 一个后置增强 AfterLog
//Log日志类,切面类   Before
public class Log implements MethodBeforeAdvice {

   //method : 要执行的目标对象的方法 method
   //objects : 被调用的方法的参数 args
   //Object : 目标对象 target
   @Override
   public void before(Method method, Object[] objects, Object o) throws Throwable {
       System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
  }
}

//==========================================================
//AfterLog日子类,切面类    AfterReturning
public class AfterLog implements AfterReturningAdvice {
   //returnValue 返回值
   //method被调用的方法
   //args 被调用的方法的对象的参数
   //target 被调用的目标对象
   @Override
   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
       System.out.println("执行了" + target.getClass().getName()
       +"的"+method.getName()+"方法,"
       +"返回值:"+returnValue);
  }
}
  • 最后去 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: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">

   <!--注册bean-->
   <bean id="userService" class="com.gwq.service.UserServiceImpl"/>
   <bean id="log" class="com.gwq.log.Log"/>
   <bean id="afterLog" class="com.gwq.log.AfterLog"/>

   <!--aop的配置-->
   <aop:config>
       <!--切入点 expression:表达式匹配要执行的方法-->
       <aop:pointcut id="pointcut" expression="execution(* com.gwq.service.UserServiceImpl.*(..))"/>
       <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
       <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
       <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
   </aop:config>

</beans>

测试

public class MyTest {
   @Test
   public void test(){
       ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
       UserService userService = (UserService) context.getBean("userService");
       userService.search();
  }
}
  • Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .

  • Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .

注解方式

@Component  //往容器创建对象
@Aspect     //生成代理对象
public class AmethodProxy {

    //可以抽取相同切入点,然后value写此类的类名
    @Pointcut(value = "execution(* com.gwq.spring5.AOP.Amethod.showName(..))")
    public void pointdemo() {    //别人引用你的切入点表达式,直接写方法名()
    }

    //后置通知(返回通知)只有return后才能得到返回值,所以其他通知不能设置returning
    @AfterReturning(value = "pointdemo()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result) {
        System.out.println("返回结果为:"+result);
    }

    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "execution(* com.gwq.spring5.AOP.Amethod.showName(..))")
    public void before(JoinPoint joinPoint) {
        System.out.println("before.........");
        //获取目标方法运行时使用的参数
        Object[] args = joinPoint.getArgs();
        System.out.println(Arrays.asList(args));
        //获取目标方法全类名
        Signature signature = joinPoint.getSignature();
        System.out.println(signature.getName());    //获取类名......
    }

    //最终通知
    @After(value = "pointdemo()")
    public void after() {
        System.out.println("after.........");
    }
    //异常通知
    @AfterThrowing(value = "pointdemo()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e) {
        System.out.println("打印错误信息:");
        e.getCause();
    }

    //环绕通知(最强大本质就是一个动态代理)
    @Around(value = "pointdemo()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Object[] args = proceedingJoinPoint.getArgs();  //获取参数
        Signature signature = proceedingJoinPoint.getSignature();   //获取签名
        String methodName = signature.getName();    //获取方法名

        Object proceed = null;
        try {
            System.out.println("【环绕前置通知】  "+methodName+"  方法执行");
            proceed = proceedingJoinPoint.proceed(args);//利用反射调用方法,就是method.invoke(obj,args)
            System.out.println("【环绕返回通知】  "+methodName+"  方法返回,返回值: "+proceed);

        }catch (Exception e){
            System.out.printf("【环绕异常通知】  "+methodName+"方法出现异常,异常信息为:");
            e.getCause();
        }finally {
            System.out.println("【环绕后置通知】"+methodName+"方法结束");
        }
        return proceed;
    }
}

alt

AOP细节

    1.代理对象和被代理对象唯一的关联就是它们的"共同接口"
    2.如果被代理对象"有接口":
        接口类型  接口对象= context.getBean("...", 接口.class);
    3.如果被代理对象"无接口",CGLIB可以实现代理
        被代理类  被代理对象= context.getBean("...", 被代理类.class);

基于XML配置AOP

    <bean id="amethodProxy" class="com.gwq.spring5.AOP.AmethodProxy"></bean>
    <bean id="amethod" class="com.gwq.spring5.AOP.Amethod"></bean>

    <aop:config>
            <!--抽取相同切入点-->
            <aop:pointcut id="pc" expression="execution(* com.gwq.spring5.AOP.Amethod.showName(..))"/>

        <!--指定第一个切面:相当于@Aspect,order表示优先级,数值越小,优先级越高-->
        <aop:aspect ref="amethodProxy" order="2">    
            <!--使用相同切入点-->
            <aop:before method="before" pointcut-ref="pc"/>
            <!--afterReturning能接收返回值,所以加上,方便代理类可以使用返回值-->
            <aop:after-returning method="afterReturning" pointcut-ref="pc" returning="result">
                                                                        </aop:after-returning>
            <!--afterThrowing能捕获异常,所以加上,方便代理类可以打印异常-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pc" throwing="e"></aop:after-throwing>
            <aop:after method="after" pointcut="execution(* com.gwq.spring5.AOP.Amethod.showName(..))">                                                                                               </aop:after>
            <!--环绕通知方法-->    
            <aop:around method="around" pointcut-ref="pc" ></aop:around>
        </aop:aspect>


        <!--指定第二个切面:相当于@Aspect-->
        <aop:aspect ref="bmethodProxy" order="1">
            .....
        </aop:aspect>
    </aop:config>

alt

(2)事务(Service层)

    (1)特 性(ACID):
        原子性、一致性、隔离性、持久性

    (2)
        脏读:第二次读了一个"没提交"的值。(不允许发生)
        不可重复读:第二次读了一个"已提交"的数据项。(允许发生)
        幻读:第二次读了一个"已提交"的值        比如:工资大于10000的记录数,提交了可能记录数变了

    (2)隔离级别:
        mysql默认可重复读REPEATABLE READ

1、声明式事务管理:底层AOP(注解方式:常用)

    (1)接口DataSourceTransactionManager:    MyBatis、spring框架使用的事务接口
    异常必须抛出才能回滚
    有事务的逻辑结构,容器中保存的是这个业务逻辑的代理对象。   

2、事务的传播行为(大事务嵌套小事务)

    <!-- 配置事务管理器 导包aspectjweaver-->
    <!--导入aop的头文件!-->

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--结合AOP实现事务的织入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
           <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
           <tx:method name="add" propagation="REQUIRED"/>
           <tx:method name="delete" propagation="REQUIRED"/>
           <tx:method name="update" propagation="REQUIRED"/>
           <tx:method name="search*" propagation="REQUIRED"/>
           <tx:method name="get" read-only="true"/>
           <tx:method name="*" propagation="REQUIRED"/>
       </tx:attributes>
    </tx:advice>


    <!--配置aop织入事务 -->
    <aop:config>
       <aop:pointcut id="txPointcut" expression="execution(* com.gwq.dao.*.*(..))"/>
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>
#学习路径#
全部评论
厉害的人真是太多了,我也要努力了
点赞 回复 分享
发布于 2022-02-21 23:37

相关推荐

牛马人的牛马人生:500一天吗?香麻了
投递字节跳动等公司6个岗位
点赞 评论 收藏
分享
12-13 14:51
已编辑
井冈山大学 算法工程师
龙虾x:算法比你强的没有你美,比你美的…..算了已经没有比你美的了
工作两年想退休了
点赞 评论 收藏
分享
评论
2
4
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务