spring 学习笔记(day-02)

一、IoC 的注解方式

1. @Componant :用来配置 Bean ,Spring 认为 Bean 就是所有的 Java 类。如果你希望当前类需要驱动 Spring 去管理,则需要使用到此注解。

此注解相当于 <bean id="" class="" />

如果说,我当前的类是一个 Service 层或者 Dao 层,又或者是 Web 层的类?

@Controller 来表示当前类是表现层的。

@Service 来表示当前类是服务层的。

@Repository 来表示当前类是持久层的。

注意:上面的这三个注解,只是让被标注的类用途清晰而已。

2.注入值的注解

当我们使用注解的方式来注入值的时候,就不需要再添加 setXxx() 方法了。

1)注入普通类型的值:@Value
	@Value(value="翠花")
	private String name;

2)注入对象类型的值:@Autowired

	@Autowired 自动装配
	// 因为一个接口,可以有多个实现类
	// 如果需要执行指定的实现类,则需要强制使用名称注入方式
	@Qualifier(value="userDao2")
	
	// 上面如果强制指定的话,需要分两步来实现,太麻烦
	// 直接使用 Resource 指定即可
	@Resource(name="userDao2")
	// 直接使用 @Resource 也是可以的,
	// 默认会执行名字跟字段一致的实现类
	@Resource
	private UserDao userDao2;
	
	// 在实际开发中,我们很少会为一个接口,添加过多的实现类
	// 一般情况,我们都是一个接口对应着一个实现类
	// 因为开发的时候,太多东西放在一个接口的时候,会很混乱

3. Bean 的作用范围

@Scope(value = "prototype") 设置多例,所有的 action 类都需要设置为多例。

4. 初始化和销毁

@PostConstract  相当于  init-method

@PreDestory  相当于  destory-method

二、AOP简介

1. AOP 的概述

        如果我们要增强功能或者迭代新版本的话,在 OOP 中只能通过集成类和实现接口的方式来实现,但会使代码的耦合度增强,并且继承类只能为单继承,这样会阻碍更多行为添加到一组类中,AOP 一定程度上,弥补了 OOP 的不足,也是 OOP 的一种延续。

        AOP 是面向切面编程,Aspect Oriented Programming.

        AOP 是由 AOP 联盟组织提出来的一套编程规范, Spirng 只是将它引入到自身框架中使用,使用的时候,必须遵守 AOP 联盟的规范。(比如,我们在使用 Spring 切面的时候,需要导入联盟包中的某些 jar 文件)

        AOP 是 Spring 框架中的一个非常重要的内容,必须要掌握的。也是函数式编程的一种衍生框架。

       还可以利用 AOP 对业务逻辑层的各个部分进行隔离,主要是为了降低耦合度。

2.AOP 的底层原理 -- 代理模式(了解)

        如果需要访问目标对象,必须通过代理的处理,当处理完了之后,再去访问目标对象获取对应的数据。

AOP 的底层代理实现,主要是有两种方式:

1)基于 JDK 的动态代理

      必须是面向接口的,只有实现了具体的接口,才能成为代理对象。

2)基于 CGLIB 的动态代理

     对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式。

      在 Spring 的 core 包中,可以看到对应的源码。

2.1 JDK 动态代理

1)添加 UserDao 接口和 UserDaoImpl 实现类。

public interface UserDao {

	public void save() throws Exception;
	
	public void update() throws Exception;
}

public class UserDaoImpl implements UserDao {

	@Override
	public void save() throws Exception {
		System.out.println("我是 save() 方法");
	}

	@Override
	public void update() throws Exception {
		System.out.println("我是 update() 方法");
		
	}
}

2)新建 MyJDKProxy 代理类。

public class MyJDKProxy {
	// 思路:先把需要代理增强的东西传进来,接着增强之后再把新的返回出去
	public static UserDao getProxy(final UserDao userDao) {
		
		// 获取类加载器
		ClassLoader loader = userDao.getClass().getClassLoader();
		
		// 获取接口
		Class<?>[] interfaces = userDao.getClass().getInterfaces();
		
		// 代理的实现
		// 后面的回调函数,执行得到的结果,
                // 当做参数给 newProxyInstance() 方法使用
		UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				
				// 把需要增强的内容,在此处执行
				if(method.getName().equals("save")) {
					
					System.out.println("我要增强了,我准备 12 块腹肌了...");
				}
				
				// 把增强的内容,进行返回
				return method.invoke(userDao, args);
			}
		});
		// 接着,把代理继续返回
		return userDaoProxy;
	}
}

3)测试类

@Test
public void fun1() throws Exception {
	UserDao userDao = new UserDaoImpl();
        // 使用 JDK 动态代理
	UserDao userDaoProxy = MyJDKProxy.getProxy(userDao);
	userDaoProxy.save();
}

2.2 CGLIB 动态代理

1)新建 AppleDao 接口和 AppleDaoImpl 实现类。(参考 UserDao 和 UserDapImpl)

2)新建 MyCGLibProxy 代理类。

public class MyCGLibProxy {
	public static AppleDaoImpl getProxy() {
		// 1. 创建 CGLib 核心类
		Enhancer enhancer = new Enhancer();
		// 2. 指定需要代理的类,也就是父类
		enhancer.setSuperclass(AppleDaoImpl.class);
		// 3. 设置回调函数
		enhancer.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
				// 需要增强的内容写到此处	
				if(method.getName().equals("save")) {	
					System.out.println("我是增强后的 save() 方法");
				}
				// 返回执行
				// 会出现一个很诡异的事情,会出现死循环
				// return methodProxy.invoke(obj, args);
				return methodProxy.invokeSuper(obj, args);
			}
		});
		// 4. 生成代理对象
		AppleDaoImpl appleDaoProxy = (AppleDaoImpl) enhancer.create();
		return appleDaoProxy;
	}
}

3)测试类

@Test
public void fun2() throws Exception {
	AppleDao userDao = new AppleDaoImpl();
        // 使用 CGLib 动态代理
	AppleDaoImpl appleDapProxy = MyCGLibProxy.getProxy();
	appleDapProxy.save();
}

3. AOP 的术语

1)连接点 joinpoint

      指到是所有的方法,也是所有的拦截点。

2)切入点 pointcut 

      主要指的是,我们需要增强的一个或者多个指定的方法。与上面的连接点有点相同,注意概念上的区分。

3)通知、增强 advice

      简单理解,我们需要增强的那个方法所对应要做的事情,就是通知。

      通知的类型有多种:

              前置通知:before

              后置通知:after-returning

              最终通知:after

              环绕通知:around

              异常通知:exception

4)引介 introduction

在不修改类代码的前提下,一种特殊的通知。

它可以在运行期为类动态添加一些方法或者 filed 字段。

5)目标对象 target 

主要指的是我们想要代理的对象,也叫目标对象。

6)织入 weaving

 指的是就是将增强的内容应用到目标对象中,然后创建新的代理对象,这个过程就是织入。注意,这是一个动态的过程。

7)代理 proxy 

一个目标对象,被 AOP 织入增强之后,就会产生一个结果代理类。

8)切面 aspect = 切入点 pointcut + 通知 advice

4. AOP 的简单使用

1)导入 AOP 联盟包

2)配置 spring-beans.xml 文件

<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"
    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/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">
</beans>

3)添加 UserDao 接口类和 UserDaoImpl 实现类(如果以前有,则无需再添加)

4)在 Demo 测试类中,注入 userDao,准备好等下可以使用。

5)添加 MyAspect 用于增强功能的切面类。

@Component(value = "myAspect")
public class MyAspect {

	public void hello() {
		
		System.out.println("我是增强后的内容..");
	}
}

6)回到 spring-beans.xml 文件中,配置 sop

<!-- 配置 AOP -->
<aop:config>
    <!-- 配置切面 = 切入点 + 通知 -->
    <aop:aspect ref="myAspect">
	<!-- 配置切入点 
		expression 表达式
		execution(想要切的方法写到这里)
		execution(访问权限 返回值类型 包路径.方法名(参数))
	-->
	<aop:before method="hello" pointcut="execution(public void com.neuedu.dao.impl.UserDaoImpl.save())"/>
	</aop:aspect> 
</aop:config>

5. AOP 的切入表达式

切入点的书写格式:

1)基本的:execlution(访问权限 返回值类型 包名.类名.方法名(参数列表))

2)访问权限可以不写,属于不是必要的选项。

3)返回值类型,不能不写。但如果觉得太麻烦,则可以使用 * 替代。

4)包名,如果太长,我们可以省略某些部分。

      a)首先,起始包路径是不能省略的,比如 com ,但可以使用 * 替代。

      b)中间的包名,就可以省略了,可以使用 * 替代。

      c)如果中间隔着多个包的话,都想一起省略,可以使用 .. 符号。

5)类名,不能省略,但是可以使用 * 替代。

6)方法名,不能省略,但是可以使用 *替代。

7)关于参数,如果是一个参数的话,则可以使用 * 替代,如果有多个参数的话,可以使用 .. 替代。

注意:在使用的时候,最好不要全部用 * 替代所有的东西,最终会搞到 aop 乱切。

6.AOP 的通知类型。

AOP 的常见通知类型如下:

        1)前置通知:before

              在方法执行之前,进行通知。

        2)后置通知:after-returning

              在方法执行之后,进行通知,如果有异常,不会执行。

        3)最终通知:after

              在目标对象中的方法执行完之后才会执行,如果程序在运行的时候出现了异常,最终通知也会执行。

         4)环绕通知:around

              可以在方法执行的前后进行通知。呈包围状态。

              如果是直接使用的话,则只会执行增强的效果。如果还需要执行目标对象中的方法,还需要我们额外手动设置让方法可以执行。

          5)异常通知:exception

猜你喜欢

转载自blog.csdn.net/weidong_y/article/details/81605969
今日推荐