Day48-Spring-02

来源:互联网 发布:农友软件 编辑:程序博客网 时间:2024/06/05 03:53

一、IOC(控制反转)的注解

ioc的注解 – 要使用注解,就要导入spring-aop-4.2.9.RELEASE.jar

1. 入门

1. 导入jar包   除了导入基本的4个 和 日志包之外, 还要导入spring-aop-xx.jar2. 给指定的类上面打上注解(打上一个标记)  
@Component("us")或@Component(value="us")        //Component : 组件  spring 把我们这些托管的类,也看成是组件        @Component("us")        public class UserServiceImpl implements UserService {        }
3. 在applicationContext.xml中 导入context约束 ,然后打开注解的扫描开关 约束去哪里找,去Spring提供的html文档中去找,搜寻context的关键字快速寻找
<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 注解的扫描开关,也就是告诉spring要去扫描指定类上面有没有注解,如果有,就解析这个注解 -->        <context:component-scan base-package="com.itheima"></context:component-scan><!-- 以下这种写法不常用。 这个开关虽然也是扫描的注解的开关,但是只能扫描在这个xml里面配置的bean 这些类 --><!--  <context:annotation-config></context:annotation-config> --></beans>
4. 获取实例的代码和以前一样。
      @Test      public void test(){           ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");           UserService userService = (UserService) context.getBean("userService");           userService.save();           ((AbstractApplicationContext) context).close();      }

2. IOC注解创建对象

spring针对ioc的注解,提供了通用的一个注解 @Component ,但是由于我们的web项目一般会使用分层的结构。 所以spring为了迎合这种趋势,也做出来每一层各自独立的注解。
@Controller : 标记 web层
@Service : 标记业务逻辑层
@Repository : 标记 dao层
spring做出来这三个独立的分层注解,也许未来会对它们进行增强、扩展,所以大家使用注解的话, 尽量使用 这三个。

  1. 如果注解里面不写内容, 那么默认也有一个id值, 就是类名的字符串,不过第一个字母小写
    注解类:
@Servicepublic class UserServiceImpl{}

取值:

context.getBean("userServiceImpl");
  1. 默认生成的实例还是单例的,如果想生成多例,那么需要配置@Scope注解
        @Service        @Scope("prototype")        public class UserServiceImpl{}
  1. 初始化方法以及销毁方法的设置
    init-method: 表示初始化这个类时,会执行指定的方法
    destroy-method : 销毁类实例时,会调用这个方法
        <bean init-method="init" destroy-method="destroy"></bean>
 (注意,只有在单例情况下,才会出现销毁的情况,就是关闭工厂。多例情况是不会执行销毁的方法的。)    这两个也可以使用注解来配置 @PostConstruct @PreDestroy   这两个配置是直接写在方法上面的
@Service("userService")@Scope("prototype")public class UserServiceImpl implements UserService {      @Override      public void save() {           System.out.println("userServiceImpl中的save方法执行了");      }      @PostConstruct      public void init(){           System.out.println("userServiceImpl中的init方法执行了");      }      @PreDestroy      public void destroy(){           System.out.println("userServiceImpl中的destroy方法执行了");      }}

3. 属性注入 – 侧重注入对象类型

使用注解属性注入:
因为这样注入普通属性,几乎没有可用性,还不如直接在声明的时候指定值

@Service("userService")@Scope("prototype")public class UserServiceImpl implements UserService {      @Value("张三")      private String username ;}

注入对象类型:
* @AutoWired

自动装配, spring会找到接口的实现类,然后创建实例注入进来
问题:如果该接口有多个实现,那么会抛出异常,无法注入;效率也没有Resource高
对象:

        @Repository("userDao")        public class UserDaoImpl implements UserDao {}使用:        @Service("us")        public class UserServiceImpl implements UserService {            @Autowired            private UserDao userDao ;       }


  • @Resource

根据指定的标签, 标识符去找到具体的类,然后注入进来。效率高
对象:
        @Repository("userDao")        public class UserDaoImpl implements UserDao {}

使用:

        @Service("us")        public class UserServiceImpl implements UserService {            @Resource(name="userDao")            private UserDao userDao ;       }

@Qualifier (了解)
用来标记 自动装配的bean在spring IoC容器中的具体名称
这个注解标记比较少用,主要是在xml + 注解结合使用,并且在xml里面针对某一种类型bean , 声明了多了,存在了多个名称。
那么可以使用@Qqualifier来强制指定使用哪一个bean来构建实例。

  <bean id="userDao" class ="com.itheima.dao.UserDao">  <property name="name" value="奥巴马">  </bean>  <bean id="userDao2" class ="com.itheima.dao.UserDao">  <property name="name" value="特朗普">  </bean>
  public class UserServiceImpl{  @Autowired  @Qqualifier(name="userDao2")  private UserDao userDao;  }

xml和注解混合使用

1.使用xml来声明bean

<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd">      <bean id="userService" class="com.itheima.service.impl.UserServiceImpl"           scope="prototype" init-method="init" destroy-method="destroy">      </bean>      <bean id="user" class="com.itheima.domain.User" scope="prototype">      </bean>      <context:component-scan base-package="com.itheima"></context:component-scan></beans>

2.使用注解来注入属性

public class UserServiceImpl implements UserService {      /*@Resource(name="user")*/      @Autowired      private User user;}public class User {      @Value("张三")      private String username;      @Value("123")      private String password;}

二、Spring测试

早前我们测试业务逻辑,一般都使用Junit 框架来测试, 其实在spring框架里面它也做出来了自己的一套测试逻辑, 里面是对junit进行了整合包装。本质上就是让我们在测试的时候少写工厂类的创建代码

使用Spring测试可以不用打开注解扫描开关, 当然如果要用的那些类上面有有注解, 就必须打开。只是测试类有注解的话,不用打开。 只有其他普通类,托管的类有注解,就必须打开注解扫描开关。
1. 导入jar包
spring -test-xxx.jar

  1. 在类上打注解
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(“classpath:applicationContext.xml”)
    这两个注解就是完成工厂的构建工作,就是指定了由SpringJUnit4ClassRunner 去读取applicationContext 完成工厂构建工作
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class SpringTest {      @Autowired      private UserService userService;      @Test      public void test(){           userService.save();      }}

三、Spring AOP – Aspect Oriented Programmig面向切面编程


  • AOP

Aspect Oriented Programming,面向切面编程 它是对OOP(面向对象编程)的一种补充,其实它的核心就是在不改动原来代码的基础上,可以进行扩展或者增强。
这里写图片描述

1. AOP的底层实现

动态代理
代理,不管是什么,一定需要先有真实对象。首先就创建一个真实对象出来

* 使用jdk的动态代理。proxy类 针对真实类,有实现接口 创建出来接口的实现类,然后创建实现类的对象作为代理对象* 使用cglib的动态代理。Enhancer类 如果那个真实类没有实现接口。 创建真实类的子类
  • 使用JDK的动态代理
    针对有实现接口的类。 创建出来接口的一个实现类,然后创建实现类的对象,作为代理对象
        @Test        public void testJDKProxy(){            //1. 创建真实对象            final UserService userService = new UserServiceImpl();            //2. 创建代理对象            UserService proxyService = (UserService) Proxy.newProxyInstance(                    userService.getClass().getClassLoader(),                    userService.getClass().getInterfaces(),                    new InvocationHandler() {                        @Override                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                            //只要在外面用了代理对象调用方法,就一定回来到这个invoke里面,这里面要做的工作就是调用真实对象的方法。                            //userService.save();                            System.out.println("增强了日志输出功能");                            return method.invoke(userService, args);                        }                    });            //3. 使用代理对象            proxyService.save();        }


  • 使用Cglib动态代理

如果那个真实类没有实现接口, 使用cglib 。 创建出来真实类的子类, 然后创建子类的对象,作为代理对象。
        @Test        public void testCglibProxy(){            System.out.println("执行了cglib代理~~");            //1. 创建真实对象            final ProductService productService = new ProductService();            //2. 创建代理            Enhancer enhancer = new Enhancer();            //设置父类是谁 表示要做谁的子类出来作为代理            enhancer.setSuperclass(ProductService.class);            //设置回调            enhancer.setCallback(new MethodInterceptor() {                @Override                public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {                    System.out.println("增强了日志输出功能2222");                    return arg1.invoke(productService, arg2);                }            });            //创建代理对象            ProductService  proxyService = (ProductService) enhancer.create();            proxyService.delete();        }

2. AOP的术语

连接点:Joinpoint
被增强的类中的所有方法都可以称之为连接点

切入点:Pointcut
具体被增强的方法

切面:Aspect
切面的解释应该是切入点和增强的结合 (这是侧重结果)。

织入:weaving
把增强应用到目标对象上来创建新的代理对象的过程
(这个注重过程)

增强:Advice
要扩展的功能。加入到切入点的方法
前置增强:增强在方法之前执行
后置增强:增强在方法之后执行
环绕增强:前后都执行增强
异常增强:只有方法出现异常才执行增强
最终增强:在最后执行。可以获取方法的返回值。
这里写图片描述

3. AOP入门

  1. 定义业务逻辑类
        public class UserServiceImpl implements UserService {            @Override            public void save() {                System.out.println("调用了UserServiceImpl 的 save方法");            }        }
  1. 定义增强类
        public class Logger {            public static void log(){                System.out.println("输出日志了~~");            }        }
  1. 导入jar包

    a. 导入 spring必须的jar

    b. 额外导入: 4个包:
    –这两个包是Spring提供的
    spring-aop-4.2.9.RELEASE.jar - spring本身的包
    spring-aspects-4.2.9.RELEASE.jar 针对aop联盟做出来的实现包,但这个实现包并不全部实现。
    – 这两个包是额外的
    aopalliance-1.0.jar 联盟包,其实就是aop规范
    aspectjweaver-1.8.9.jar - 实现包
    面向切面过程中,Spring AOP是遵循了AOP联盟的规范实现的,所以需要有AOP联盟的接口包
    aopalliance-x.x.jar,接口包依赖aspectjweaver-x.x.x.jar

  2. 导入约束
    要导入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"      xmlns:tx="http://www.springframework.org/schema/tx"      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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">      <bean id="userService" class="com.itheima.service.impl.UserServiceImpl">      </bean>      <bean id="logger" class="com.itheima.domain.logger">      </bean></beans>
  1. 让spring托管 业务逻辑类 和 增强类
<bean id="us" class="com.itheima.service.impl.UserServiceImpl" ></bean><bean id="logger" class="com.itheima.util.Logger" ></bean>
  1. 配置AOP
         <!-- 下面的配置核心,就是把logger里面的log方法应用到us里面的save方法中 -->        <!-- 开始aop的配置 -->        <aop:config>            <!-- 就是用于表示,要对哪一个方法进行增强。 其实就是定义一种规则,然后spring根据这种规则就能够找到对应的方法            后面将要对这些找到的方法进行增强                execution(* com.xyz.myapp.service.*.*(..))                execution  : 固定写法,表示执行                第一个*  : 表示任意返回值                com.xyz.myapp.service : 表示这个包                第二个* : 表示上面这个包的任意类                第三个* 表示上面找到的类的任意方法                (..) : 表示任意参数                spring根据这个表达式已经找到了具体对应的方法。            -->            <aop:pointcut expression="execution(* com.itheima.service.impl.*.*(..))" id="savePointCut"/>            <!-- 还缺少,把什么方法应用到上面找到的那些方法之上,去完成增强                连起来的意思就是: 把一个叫做logger的这个bean 里面的一个方法叫做 log 的方法,用到 一个叫做savePointCut 切入点找到                的那些方法上面去,执行前置增强             -->            <aop:aspect ref="logger">                <!-- 定义前置增强                    根据上面的表达式找到的切面,其实就是找到的那些方法,给他们应用前置增强, 把一个叫做log的方法用到他们前面去。                -->                <aop:before method="log" pointcut-ref="savePointCut"/>            </aop:aspect>        </aop:config>

4. 增强

   <!-- 配置aop -->    <aop:config>        <!-- 配置切入点 spring根据这个规则,找到具体的方法            xml方式不好控制具体哪一个方法        -->        <aop:pointcut expression="execution(* com.itheima.service.impl.*.save(..))" id="pointCut01"/>        <!-- 这个是对事务操作时,才有用 -->        <!-- <aop:advisor advice-ref=""/> -->        <aop:aspect ref="logger">            <!-- 前置增强 -->            <!-- <aop:before method="log" pointcut-ref="pointCut01"/> -->            <!-- 后置增强 -->            <!-- <aop:after method="log" pointcut-ref="pointCut01"/> -->            <!-- 环绕增强 -->             //环绕异常也可以同时写上前置增强 和 后置增强,即为环绕异常            <!-- <aop:around method="around" pointcut-ref="pointCut01"/> -->            <!-- 异常增强 -->            <!-- <aop:after-throwing method="log" pointcut-ref="pointCut01"/> -->            <!-- 最终增强 -->             <aop:after-returning returning="result" method="log02"  pointcut-ref="pointCut01"/>        </aop:aspect>