Spring注解开发
在spring4 后,使用注解开发就需要保证导入了aop包
并加入 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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注解的支持-->
<!--扫描指定的包,包中的注解会生效-->
<context:component-scan base-package="com.xmn"/>
<context:annotation-config/>
</beans>
属性的注入
//相当于 <bean id="user" class="com.xmn.pojo.User"/>
@Component
public class User {
// 相当于 <property name="name" value="Xmn"/>
@Value("Xmn")
public String name;
}
衍生注解
@Component有几个衍生的注解,在web开发中,按照MVC分为三层。
- dao【@Repository】
- service【@Service】
- controller【@Controller】
这四个注解的功能都是将某个类注册到Spring中。
作用域
//相当于 <bean id="user" class="com.xmn.pojo.User"/>
@Component
//@Scope指定作用域 singleton指单例模式 prototype 指多实例
@Scope("singleton")
public class User {
// 相当于 <property name="name" value="Xmn"/>
@Value("Xmn")
public String name;
}
小结
XML 功能更强大,适用于任何场合,方便维护.
注解 只能使用自己类中的东西,维护复杂。
最好使用 xml 来管理bean,而注解只用来完成属性的注入。
使用Java方式配置Spring
不使用 XML文件配置Spring,全交给java来做,用到了javaconfig。
定义实体类:
//这个注解的意思是这个类被Spring接管了
@Component
package com.xmn.pojo;
import org.springframework.beans.factory.annotation.Value;
public class User {
private String name;
public User(String name) {
this.name = name;
}
@Value("xmn") //注入属性值
public void setName(String name) {
this.name = name;
}
public User() {
}
public String getName() {
return name;
}
}
创建一个config类
import com.xmn.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//@Configuration意为这是一个配置类
@Configuration
@Compomentscan(com.xmn.pojo)
public class Xmnconfig {
// 注册一个bean,相当于配置文件中的bean标签
// 方法的名字就是bean标签中的 id属性
// 方法的返回值就是bean标签中的class属性
@Bean
public User user(){
return new User();
}
}
代理模式
AOP的底层就是听过代理模式实现的。
代理模式分为静态代理和动态代理两种。
静态代理
角色分析:
- 抽象角色:一般由接口和抽象类来实现
- 真实角色:被代理的角色
- 代理角色:代理别人的角色,用来做到一些操作
- 客户:访问代理角色的人。
什么是静态代理呢?
两个对象之间的操作不直接进行,通过第三方进行代理来实现。比如房东要出租房子,那么房屋中介就是静态代理。
为什么需要静态代理呢?
对于房东出租房子,我们可以这样实现
package com.xmn.demo01;
public interface Rent {
public void rent();
}
package com.xmn.demo01;
public class Host implements Rent{
public void rent(){
System.out.println("出租房子。");
}
}
但是如果我们需要增加一些功能时,如果直接在源码上修改,很容易产生很多错误。而静态代理可以在不改变源码的基础上完成更改。
package com.xmn.demo01;
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
}
public void seeHouse(){
System.out.println("看房子");
}
}
在出租房子之前可以增加一个看房子的流程,但是源码并没有被修改。
静态代理模式的优点:
- 使真实角色更纯粹,不用关注公共业务。
- 公共业务交给代理角色处理,实现业务分工。
- 公共业务扩展方便集中管理。
静态代理模式的缺点:
每个真实角色都需要一个代理角色,代码量翻倍,开发效率降低。
动态代理
动态代理的角色与静态代理相同,真实角色,代理角色和抽象角色。
与静态代理不同的是,动态代理不用为每个真实角色编写代理角色,他通过InvocationHandler的实现类与反射包中的Proxy类来获取真实角色的代理角色。
测试代码如下:
抽象角色与真实角色:
package com.xmn.demo02;
public interface UserService {
public void add();
public void del();
public void upd();
public void que();
}
//----------------------------------------------------------------------------
package com.xmn.demo02;
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("add");
}
public void del() {
System.out.println("del");
}
public void upd() {
System.out.println("upd");
}
public void que() {
System.out.println("que");
}
}
动态代理的实现:
package com.xmn.demo04;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getTarget() {
return target;
}
//获取代理类的方法
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//实现InvocationHandler接口后,需要实现invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("调用了"+ msg +"方法");
}
}
使用代理的方法:
package com.xmn.demo04;
import com.xmn.demo02.UserService;
import com.xmn.demo02.UserServiceImpl;
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService);
UserService proxy = (UserService) pih.getProxy();
proxy.que();
}
}
测试结果:
使用Spring实现AOP
需要导入依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
1、使用Spring的API接口
环境搭建
定义UserService接口和他的实现类
package com.xmn.service;
public interface UserService {
public void add();
public void del();
public void upd();
public void que();
}
//-----------------------------------------------------------
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("add");
}
public void del() {
System.out.println("del");
}
public void upd() {
System.out.println("upd");
}
public void que() {
System.out.println("que");
}
}
定义类实现Spring的API接口
想要在方法执行前插入的操作,写在Log类中
Log类实现 MethodBeforeAdvice 接口
package com.xmn.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() +"的"+ method.getName()+"方法被执行了。");
}
}
想要在方法执行后插入的操作,写在AfterLog类中
AfterLog类实现 AfterReturningAdvice 接口
package com.xmn.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了 " + method.getName()+"方法,返回值为:"+returnValue );
}
}
在配置文件中配置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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="UserService" class="com.xmn.service.UserServiceImpl"/>
<bean id="Log" class="com.xmn.log.Log"/>
<bean id="AfterLog" class="com.xmn.log.AfterLog"/>
<!--配置aop-->
<aop:config>
<!--切入点 expression:表达式 execution:要执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* com.xmn.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="Log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="AfterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试代码:
import com.xmn.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口 所以得到接口类型的 如果返回实现类类型,会报错
UserService userService = context.getBean("UserService", UserService.class);
userService.add();
}
}
测试结果:
2、自定义类实现AOP
自定义切面
package com.xmn.diy;
public class DiyPointcut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
配置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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="UserService" class="com.xmn.service.UserServiceImpl"/>
<bean id="diy" class="com.xmn.diy.DiyPointcut"/>
<aop:config>
<!--自定义的切面-->
<aop:aspect ref="diy">
<aop:pointcut id="pointcut" expression="execution(* com.xmn.service.UserServiceImpl.*(..))"/>
<!--方法执行前-->
<aop:before method="before" pointcut-ref="pointcut" />
<!--方法执行后-->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
测试代码:
import com.xmn.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = context.getBean("UserService", UserService.class);
userService.add();
}
}
运行结果
3、注解实现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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--注册bean-->
<bean id="UserService" class="com.xmn.service.UserServiceImpl"/>
<bean id="Log" class="com.xmn.log.Log"/>
<bean id="AfterLog" class="com.xmn.log.AfterLog"/>
<!--方式三-->
<bean id="AnnotationPointcut" class="com.xmn.diy.AnnotationPointcut"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
</beans>
定义注解切入类
package com.xmn.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect //标注切面
public class AnnotationPointcut {
@Before("execution(* com.xmn.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前");
}
@After("execution(* com.xmn.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后");
}
//在环绕切入中,我们可以给定一个参数,来代表我们要获取的处理切入的点
@Around("execution(* com.xmn.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//代表执行方法
jp.proceed();
System.out.println("环绕后");
}
}
测试代码:
import com.xmn.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = context.getBean("UserService", UserService.class);
userService.add();
}
}
执行结果:
可见执行顺序为,环绕前,Before,环绕后,After。