您的位置:首页 > 编程语言 > Java开发

Spring入门(二)— IOC注解、Spring测试、AOP入门

2018-02-19 21:59 399 查看

一、Spring整合Servlet背后的细节

1.为什么要在web.xml中配置listener

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>



配置listener主要是为了捕获项目发布|服务器启动的契机,为了解析xml,创建工厂。这个listener是spring官方提供的,里面已经具备了解析xml和创建工厂的代码。


2.为什么要在web.xml中配置context-param

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>



正如上面所说的,listener捕获到了项目发布的契机,进而去创建工厂。但是创建工厂需要解析xml。spring的这个监听器里面,默认会到WEB-INF/applicationContext.xml.如果不想放置到这个位置,可以通过一个context-param来告诉spring,我们的配置文件在哪里。
classpath
表示这个文件是位于类路径底下。classes目录


3.为什么使用工具类也能拿到工厂,到底工厂放在了哪里?  

publicclassContextLoaderListenerextendsContextLoaderimplementsServletContextListener{

//项目一发布,就执行
publicvoidcontextInitialized(ServletContextEventevent){
//创建工厂
initWebApplicationContext(event.getServletContext());
}
}
​
publicWebApplicationContextinitWebApplicationContext(ServletContextservletContext){
...
if(this.context==null){
//创建工厂
this.context=createWebApplicationContext(servletContext);
}

//把创建好的工厂,存储到作用域
//servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
this.context);
...
}
​
//spring为了简化程序员获取工厂的工作,就提供了一个工具类,其实这个工具类就是对取值的代码进行封装
ApplicationContextcontext=WebApplicationContextUtils.getWebApplicationContext(getServletContext());

//ApplicationContextcontext=(ApplicationContext)getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
​




二、IOC注解

1.注解入门

导入jar包

spring-aop-xxx.jar


导入约束

<beansxmlns="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.xsdhttp://www.springframework.org/schema/context       http://www.springframework.org/schema/context/spring-context.xsd">


在xml里面打开注解扫描开关

<!--告诉spring要去解析类,因为类上有注解-->
<context:component-scanbase-package="com.pri.service.impl"/>


在托管的类上打注解

@Component("us")
publicclassUserServiceImplimplementsUserService{
...
}


问工厂要实例对象

//创建工厂创建工厂,需要解析xml,所以要传递进去xml文件
ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");

//问工厂要实例对象
UserServiceuserService=(UserService)context.getBean("us");

//调用方法
userService.save();

//关闭工厂:以后我们几乎不会关闭工厂。
((AbstractApplicationContext)context).close();


2.注解创建对象

/*
1.@Component组件,表示要托管这个类,让spring创建这个类的实例。括号里面的us其实就是id标识符
2.@Component是通用的注解,对任何托管的类都可以使用,但是spring为了迎合三层架构,所以对每一层
也给出了具体的注解。
Action---@Controller
Service---@Service
Dao---@Repository:仓库,
建议:如果以后托管三层中的类,请使用具体对应的注解,如果托管的是普通的其他类。@Component
3.默认生成的实例还是单例,如果想做成多例,那么还得添加一个注解
@Scope("prototype")
4.@PostConstruct//初始化实例的时候调用
@PreDestroy//销毁实例之前,调用
5.如果使用注解托管某一个类,不写属性值,那么默认的id标识符就是类的名字(首字母是小写)userServiceImpl
6.<!--如果想要扫描多个包,就写一个通用的前缀即可-->
<context:component-scanbase-package="com.pri"/>
*/


3.DI注解开发(注入对象)


使用注解来完成依赖注入。一般注解注入,它针对的点是对象的注入。spring针对对象的注入,提供了两个注解
@Resource
@Autowired




常用的注解就两个@Resource&@Autowired

@Resource(name="ud")根据给定的标记找到对应的类,创建对象,注入进来。

@Autowired自动装配,会找到对应的实现类创建对象,注入进来。但是如果存在多个实现,那么会抛出异常

@Repository("ud")
publicclassUserDaoImplimplementsUserDao{
}

publicclassUserServiceImplimplementsUserService{
@Resource(name="ud")//spring拿着ud找到具体的类,然后创建实例,注入进来。
privateUserDaouserDao;
...
}
----------------------------------------------------------
publicclassUserServiceImplimplementsUserService{
@Autowired//自动装配根据注入的接口类型找到对应的实现类,注入进来。
privateUserDaouserDao;
...
}


4.xml和注解混合使用


在项目里面,使用xml和注解来完成Spring的配置。

xml:负责完成IOC(对象的创建)

注解:负责完成DI(属性的注入)


xml托管类


<context:component-scanbase-package="com.pri"/>
<beanid="ud"class="com.pri.dao.impl.UserDaoImpl"></bean>
<beanid="us"class="com.pri.service.impl.UserServiceImpl"></bean>


注解完成注入:


publicclassUserServiceImplimplementsUserService{
@Resource(name="ud")
privateUserDaouserDao;
}


三、Spring测试

导入jar包

spring-test-xxx.jar


托管业务逻辑类,不管是用xml还是注解都可以

<beanid="us"class="com.pri.service.impl.UserServiceImpl"></bean>


在测试类上打上注解,给测试类的成员变量注入值

//spring扩展了junit的运行环境,除了有测试功能之外,还在里面定义了创建工厂的代码
@RunWith(SpringJUnit4ClassRunner.class)
​
//告诉spring的测试环境,配置文件在哪里
@ContextConfiguration("classpath:applicationContext.xml")
publicclassTestUserService{

//测试类里面出现的注解,不用打开扫描开关。因为这个测试环境里面,它会解析这个测试类的注解。
@Autowired
privateUserServiceuserService;
@Test
publicvoidtestSave(){
userService.save();
}
}


四、AOP

1.什么是AOP,它有什么用?


AOP(AspectOrientedProgramming,面向切面编程),可以说是OOP(ObjectOrientedPrograming,面向对象编程)的补充和完善。OOP更多的是侧重于上下间的关系(继承关系、实现关系),OOP很难体现左右间的关系。核心:在不改动源码的前提下,对原有功能能完成扩展|升级


  


2.AOP的底层原理


aop不改源码,但是能够扩展和升级代码。能够做成这个事情,只有三种解决手法:
装饰者模式
静态代理
动态代理
。AOP选择的是动态代理,装饰者模式和静态代理,要求我们必须写出来装饰类和代理类。动态代理的实现机制,有两种:
基于JDK的动态搭理
基于Cglib的动态代理




1.动态代理的实现机制

基于JDK的动态代理


如果哪一个真实类有实现接口,那么就采用这种方式,创建出来接口的另一个实现类作为代理类



//jdk动态代理
@Test
publicvoidtestJDKPorxy(){

//UserServiceuserService=newUserServiceImpl();
//userService.save();

//1.先创建真实对象
finalUserServiceuserService=newUserServiceImpl();

//2.创建代理对象
UserServiceproxyObj=(UserService)Proxy.newProxyInstance(
userService.getClass().getClassLoader(),//类加载器,真实类用什么,代理类就用什么
userService.getClass().getInterfaces(),//真实类实现什么接口,代理类也实现什么接口
newInvocationHandler(){//回调函数
@Override
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
System.out.println("invoke~");
//userService.save();

if(method.getName().equals("save")){
Logger.log();
}

//以不变应万变。反射调用
returnmethod.invoke(userService,args);
}
});

//3.让代理对象干活
proxyObj.save();//代理对象。save()---->真实对象.save();
}


基于Cglib动态代理


如果真实类是一个普通类,没有实现接口,那么就采用这种方式,创建出来真实类的子类作为代理类。



//cglib动态代理
@Test
publicvoidtestCglibPorxy(){
//1.一定要有真实对象
finalProductServiceproductService=newProductService();
//2.创建代理
Enhancerenhancer=newEnhancer();
//设置父类是谁
enhancer.setSuperclass(ProductService.class);
//设置回调
enhancer.setCallback(newMethodInterceptor(){
/*
*arg0:代理对象
*arg3:方法的代理
*
*一般这两不用。
*
*arg1:方法引用
*arg2:参数
*/
@Override
publicObjectintercept(Objectarg0,Methodarg1,Object[]arg2,MethodProxyarg3)throwsThrowable{
Logger.log();
returnarg1.invoke(productService,arg2);
}
});

//创建代理对象
ProductServiceproxyObj=(ProductService)enhancer.create();
proxyObj.save();
}


3.AOP术语



4.AOP的入门


Spring的AOP其实已经准备好了创建代理的代码。只是不知道的是要创建谁的代码。哪些方法需要被增强。我们需要通过配置的形式告诉spring。



定义业务逻辑类

publicclassUserServiceImplimplementsUserService{
​
@Override
publicvoidsave(){
System.out.println("调用了UserServiceImpl的save方法");
}
}


定义增强类

publicclassLogger{
publicstaticvoidlog(){
System.out.println("输出日志了~~");
}
}


导入jar包

a.导入spring必须的jar
​
b.额外导入:
​
spring-aop-xx.jar,spring-aspect-xx.jar
​
面向切面过程中,SpringAOP是遵循了AOP联盟的规范实现的,所以需要有AOP联盟的接口包
aopalliance-x.x.jar,接口包依赖aspectjweaver-x.x.x.jar


xml中配置


要导入aop的约束


让spring托管业务逻辑类和增强类


<beanid="us"class="com.pri.service.impl.UserServiceImpl"></bean>
<beanid="logger"class="com.pri.util.Logger"></bean>​


配置AOP


<!--2.开始配置aop-->
<aop:config>
<!--配置切入点expression表达式'
execution(*com.xyz.myapp.service.*.*(..))
execution固定写法
第一个*代表任意返回值
com.xyz.myapp.service:包名
第二个*包下的任意类
第三个*类中的任意方法
(..):任意参数
saveUser
saveOrder
-->
<aop:pointcutexpression="execution(*com.pri.service.impl.*.*(..))"id="aa"/>
<!--配置增强
根据aa的表达式找到的方法,都给他们做前置增强,增强的功能是log方法
-->
<aop:aspectref="logger">
<aop:beforemethod="log"pointcut-ref="aa"/>
</aop:aspect>
</aop:config>


5.AOP增强

<aop:config>
  <aop:pointcutexpression="execution(*com.pri.service.impl.*.*(..))"id="pointcut01"/>
<!--真正用aop来扩展一个功能,比较少。除非是我们想扩展第三方jar包。
aop的思想无处不在:struts拦截器(就是AOP)-->

<!--配置切面aspect-->
<aop:aspectref="logger">
<!--前置增强-->
<!--<aop:beforemethod="log"pointcut-ref="pointcut01"/>-->

<!--最终增强-->
<!--<aop:aftermethod="log"pointcut-ref="pointcut01"/>-->

<!--后置增强-->
<!--<aop:after-returningmethod="log"pointcut-ref="pointcut01"/>-->

<!--异常增强-->
<!--<aop:after-throwingmethod="log"pointcut-ref="pointcut01"/>-->

<!--环绕增强-->
<!--<aop:aroundmethod="around"pointcut-ref="pointcut01"/>-->
<aop:beforemethod="log"pointcut-ref="pointcut01"/>
<aop:after-returningmethod="log"pointcut-ref="pointcut01"/>
</aop:aspect>
</aop:config>



                                            
                                        
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: