1.IOC+DI
什么是IOC?
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,IOC意味着将你准备好的对象(或者是写好的类)交给容器控制,而不是传统的在你的对象内部直接进行控制。也就是说将对象的创建权反转(交给)给Spring。
什么是DI?
DI—Dependency Injection,即“依赖注入”,组件之间的依赖关系由容器在运行期决定。形象的说,即由容器动态的将某个依赖关系注入到组件中。依赖注入并非是为了给软件系统带来更多的功能,而是为了提高组件的重用效率,并为系统搭建一个灵活、可扩展的平台。
甚至,可以说IOC思想是通过DI技术实现的,或者说DI是另一种说法的IOC。
理解DI需要知道的是:“谁依赖谁,为什么需要依赖,谁注入了谁,注入了什么”
- 谁依赖谁:当然是应用程序依赖于IOC容器(spring容器)
- 为什么要依赖:应用程序需要IOC容器来提供对象需要的外部资源
- 谁注入了谁:当然是IOC容器注入了某个应用程序依赖的对象到应用程序中
- 注入了什么:注入某个对象所需要的外部资源(包括对象、资源、常量数据)
举例表达:
IOC:你要去学校找一个老师。要在校门口的门卫室告诉门卫你需要找谁,因为所有的老师信息都在这里登记过。这里的门卫室就类似于SpringIoc容器,会把所有的对象类注册。在你需要的时候即你提到某个老师的名字的时候,SpringIoc容器才会把你需要的东西。也就是说所有类的创建、销毁都由Spring来控制,也就是说控制对象生命周期的不再是对象,而是Spring,这就是控制反转。
DI:你找的老师名字是张三。然后门卫记录册上翻到了,于是门卫给老师打电话喊了过来。此时这个老师就和你口中的张三对上号了,这就是注入。当然了老师也是有属性的,你可以喊一个男的张三老师,男的教语文的张三老师。这些属性都是可以在记录册上注册在案的。当然了这个在 SpingIoc容器中并不是提前准备好的张三老师,而是在需要的时候生成的,然后注入到需要的对象中,这样就通过DI技术完成了对象的控制。此时,某个(学校)类需要张三老师这个对象完成某些(教学)方法,而张三老师这个对象是由SpringIoc容器注入到某个(学校)类中的,依赖注入就由此而来。Java1.3以后出现的一个重要特征就是反射(reflection),它允许程序在运行时动态的生成对象、执行对象的方法、改变对象的属性,Spring就是通过反射来实现注入的。
如果整理的太过凌乱,还是无法完全理解,看这篇博客。
接下来代码演示:
1.对象类
package com.no_interface.demo;
public class BookService {
public void addBook() {
System.out.println("add Book");
}
}
2.配置文件
<?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 definitions here -->
<!-- 配置Service
<bean>配置需要创建的对象
id:用于之后从spring容器中获得实例
class:需要创建的全限定类名
-->
<bean id="BookServiceId" class="com.no_interface.demo.BookService"></bean>
</beans>
3.测试类
package com.no_interface.demo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIoc {
@Test
public void demo1() {
//之前开发
BookService bookService = new BookService();
bookService.addBook();
}
@Test
public void demo2() {
//spring容器获得
//1.获得容器,默认路径再src下,因此复制完整路径以后可以删除src之前的
String xmlPath = "com/no_interface/demo/beans.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//2.获得内容,不需要自己new,从spring容器中getBean
BookService userService = (BookService) applicationContext.getBean("BookServiceId");
userService.addBook();
}
}
2.AOP
什么是AOP?
AOP——Aspect Oriented Programming,面向切面编程。简单来说就是"横向重复,纵向抽取"。在程序中主要用来解决一些系统层面的问题,比如日志,事务。权限等。如果不懂的话几个例图解决:当某一功能重复使用冗余代码量时,可以将其相同流程抽出。然后后面在需要的地方注入。这就是所谓的AOP
AOP基本概念:
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)Proxy(代理):
(6)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类(但不能被final修饰)。优先使用动态代理。
代码演示:
1.接口
package com.spring_day2.aop;
public interface UserService {
public void save();
public void delete();
public void update();
public void find();
}
2.实现类(目标对象类)
package com.spring_day2.aop;
/**
* 目标对象类
* @author 33452
*
*/
public class UserServiceImpl implements UserService {
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("保存用户");
int i = 1/0;
}
@Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除用户");
}
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("更新用户");
}
@Override
public void find() {
// TODO Auto-generated method stub
System.out.println("查找用户");
}
}
3.通知类
package com.spring_day2.aop;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 通知类
* @author 33452
*
*/
public class MyAdvice {
/**
* 前置通知 |-目标方法运行之前调用
* 后置通知(如果出现异常,不会调用) |-目标方法运行之后调用
* 环绕通知 |-目标方法运行之前和之后都调用
* 异常拦截通知 |-目标方法出现异常调用
* 后置通知(无论是否出现异常,都会调用) |-目标方法运行之后调用
*/
//前置通知
public void before() {
System.out.println("前置通知");
}
//后置通知
public void afterNoExceptionReturning() {
System.out.println("后置通知(方法出现异常,不会调用)");
}
//环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知之前的部分");
Object proceed = pjp.proceed();//这是调用目标方法代码,不要问,记住,我不知道为啥
System.out.println("环绕通知之后的部分");
return proceed;
}
//异常通知
public void afterException() {
System.out.println("异常通知");
}
//后置通知
public void afterAnythingReturning() {
System.out.println("后置通知(方法出现异常,也会调用)");
}
}
4.配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<!-- 准备工作,引入xmlns:aop命名空间(aop约束) -->
<!-- 配置目标对象 -->
<bean name="userServiceImpl" class="com.spring_day2.aop.UserServiceImpl"></bean>
<!-- 配置通知对象 -->
<bean name="myAdvice" class="com.spring_day2.aop.MyAdvice"></bean>
<!-- 配置将通知织入目标对象 -->
<aop:config>
<!-- 配置切入点
表达式 public void com.spring_day2.aop.UserServiceImpl.save()
void com.spring_day2.aop.UserServiceImpl.save() (默认服务public)
* com.spring_day2.aop.UserServiceImpl.save() (对返回值不做要求)
* com.spring_day2.aop.UserServiceImpl.*() (表示针对该类下所有空参方法)
* com.spring_day2.aop.UserServiceImpl.*(..) (表示对方法参数不做要求)
最终形态:
* com.spring_day2.aop.*ServiceImpl.*(..) (表示对该包下所有类名含有ServiceImpl的类的所有方法)
* com.spring_day2.aop..*ServiceImpl.*(..) (表示对该包下以及该包下的子包)
-->
<aop:pointcut expression="execution(* com.spring_day2.aop.*ServiceImpl.*(..))" id="pc"/>
<!-- 描述通知 -->
<aop:aspect ref="myAdvice">
<!-- myadvice中的before方法指定aop的前置通知before方法,然后切入到切入点中 -->
<!-- 前置 -->
<aop:before method="before" pointcut-ref="pc"/>
<!-- 后置(无异常) -->
<aop:after-returning method="afterNoExceptionReturning" pointcut-ref="pc"/>
<!-- 异常拦截通知 -->
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pc"/>
<!-- 后置(有异常) -->
<aop:after method="afterAnythingReturning" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
5.测试类
package com.spring_day2.aop;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
//测试类
@RunWith(SpringJUnit4ClassRunner.class)//新建spring容器
@ContextConfiguration("classpath:com/spring_day2/aop/applicationContext.xml")//指定容器使用的配置文件
public class TestDemo {
@Resource(name="userServiceImpl")
private UserService userServiceImpl;
@Test
public void demo() {
userServiceImpl.delete();
}
}
题外话:
Spring需要导入的jar包(核心、依赖):
|—4个核心
|—2个依赖
Spring Aop需要导入的包:
|—2个核心
|—以及2个第三方包
Spring测试类需要导入的包: