一:不注入属性的一种写法
(1)引入依赖,aop.jar
(2)创建类及相关属性
package com.sj.computer.www.myspringdemo6;
import org.springframework.stereotype.Service;
@SuppressWarnings("all")
@Service(value = "student123123")
//这种写法和 是一样的
//只不过不用id了换成了value,有了这个xml解析时会扫描指定包下面的所有类,找到带有注解的创建对象
//这个value的名字可以随便起,不写value默认是类名首字母小写
public class Student {
private String name;
private String sex;
private Course course;
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setCourse(Course course) {
this.course = course;
}
}
(3)创建并配置XML文件
(4)测试是否获得了对象
public class test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/sj/computer/www/myspringdemo6/demo6.xml");
Student student = context.getBean("student123123", Student.class);
System.out.println(student);
}
}
com.sj.computer.www.myspringdemo6.Student@2aceadd4
二:注入属性
(1)@Autowired:根据属性类型进行自动装配
一般来说不能单独使用Autowired、Qualifier,如果有多个同样的实现接口的类,那么你根据类型是找不到具体的哪个,要和@Qualifier(value = “xxxxx…”)一起用
(1)保证要引用的对象对应类会被XML扫描
加注解或配置XML
@Repository
public class Course {
private Integer id;
private String name;
}
在Student属性需要引用外部对象前加@Autowired根据类型注入
@Autowired
//根据类型注入,XML匹配所有被扫描的类中有无Course。
//有就把Course类的对象地址赋值到这里(前面学过的知识,XML在解析时会创建每一个bean标签的实例)
private Course course;
一般来说不能单独使用Autowired,如果有多个同样的实现接口的类,那么你根据类型是找不到具体的哪个
在Student属性需要引用外部对象前加@Autowired根据类型注入
@Autowired
@Qualifier(value = "course")
private Course course;
com.sj.computer.www.myspringdemo6.Student@305b7c14
(2)@Qualifier:根据属性名称进行注入,和@Autowired一起使用
@Repository(value = "course") //不写value默认是该类名字首字母小写,这里为了演示清楚,我就写了
public class Course {
private Integer id;
private String name;
}
@Autowired
@Qualifier(value = "course")
//根据属性名注入
//有value:course就把Course类的对象地址赋值到这里(前面学过的知识,XML在解析时会创建每一个bean标签的实例)
private Course course;
com.sj.computer.www.myspringdemo6.Student@710f4dc7
(3)@Resource:可以根据类型注入,可以根据名称注入
(1)引入jar包,在tomcat的lib下,第一个annotations-api.jar,添加为库即可使用。
//@Resource 默认根据类型
//@Resource(name = "course") 根据名称注入
(2)这个不是springFramework里面的,是JavaX里的(tomcat9),不建议使用,当然功能能实现。
(4)@Value:注入普通类型属性
@Value("001")
private Integer id;
@Value("科技部")
private String name;
Student{name='null', sex='null', course=Course{id=1, name='科技部'}}
三:使用配置类,完全使用配置来开发替代XML(在实际开发中,使用springboot来完成)
(1)自定义一个类,加上注解,把该类变成配置类,作用等同于XML文件
@Configuration//声明这是一个配置类
@ComponentScan(basePackages = {"com.sj.computer.www.myspringdemo7"})//扫描路径中带有4大注解的类
public class SpringConfig {
}
(2)自定义类和属性,加上bean注解
(3)测试,在读取的时候和XML不一样
public static void main(String[] args) {
//AnnotationConfigApplicationContext读取,SpringConfig.class类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
Student student = context.getBean("student", Student.class);
System.out.println(student);
}
(1)创建接口
package com.sj.computer.www.myspringdemo8;
@SuppressWarnings("all")
public interface Testdao {
String say(String name);
}
(2)创建接口实现类
package com.sj.computer.www.myspringdemo8;
public class TestdaoImpl implements Testdao{
@Override
public String say(String name) {
return "你好:"+name+"!";
}
}
(3)使用Proy类创建接口代理对象
一:什么叫动态代理?
答:你不需要写实现接口类的类,就可以实现接口方法。
动态代码:我们仍然先定义了接口Hello
,但是我们并不去编写实现类,而是直接通过JDK提供的 一个Proxy.newProxyInstance()
创建了一个Hello
接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。
原本:
CharSequence cs = new StringBuilder();
接口 实现类
我们写实现类的目的不就是实现接口的方法嘛,现在你可以不需要写这个类,就能拥有,现在
通过
Proxy.newProxyInstance()
创建
interface
就是我们的接口对象(你可以理解为是之前得实现类对象)
实例,它需要3个参数:
ClassLoader
,通常就是接口类的ClassLoader
;InvocationHandler
实例。将返回的Object
强制转型为接口。
以上是原理,现在我要在原本的实现类中的方法被执行的时候同时能够执行我添加的代码,以下是我的具体实现代码:
(1)接口类
package com.sj.computer.www.myspringdemo8;
@SuppressWarnings("all")
public interface Testdao {
void say(String name);
}
(2)实现类
package com.sj.computer.www.myspringdemo8;
public class TestdaoImpl implements Testdao{
@Override
public void say(String name) {
System.out.println("你好我是第1行!");
System.out.println("你好我是第2行!");
System.out.println("你好我是第3行!");
System.out.println(name);
}
}
(3)测试类
package com.sj.computer.www.myspringdemo8;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
@SuppressWarnings("all")
public class MyProxy {
public static void main(String[] args) {
Class[] interfaces = {Testdao.class};
TestdaoImpl testdao = new TestdaoImpl();
//我们知道返回的是TestdaoImpl就直接把object换成TestdaoImpl
Testdao obj = (Testdao) Proxy.newProxyInstance(Testdao.class.getClassLoader(), interfaces,new MyProxyImpl(testdao));
//你调用obj的方法,它会自动调用invoke方法
obj.say("李四");
}
static class MyProxyImpl implements InvocationHandler {
private Object obj;
public MyProxyImpl(Object obj) {
this.obj = obj;
}
//在invoke写增强的逻辑代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy:testdao
//method:Testdao.say
//args:[0]->"李四"
if(method.getName().equals("say")){
System.out.println("此方法执行前要执行这段代码"+method.getName()+":"+ Arrays.toString(args));
//原本Testdao接口执行say方法时候,只用执行invoke方法里的method.invoke就行了
// 现在invoke方法又多了几行代码,所以它也得执行
Object invoke = method.invoke(obj, args);
System.out.println("此方法执行后要执行这段代码"+method.getName()+":"+Arrays.toString(args));
}
return null;
}
}
}
此方法执行前要执行这段代码say:[李四] 你好我是第1行! 你好我是第2行! 你好我是第3行! 李四 此方法执行后要执行这段代码say:[李四]
1、连接点
类里面哪些方法可以被增强,这些方法称为连接点
2、切入点
实际被真正增强的方法,称为切入点
3、通知(增强)
(1)实际增强的逻辑部分称为通知(增强)
(2)通知有多钟类型
*前置通知:在方法执行前执行 before
*后置通知:在方法执行完后执行 after
*环绕通知:在方法执行前后都执行
*异常通知:在方法执行发生异常时执行
*最终通知:不管有无异常都执行类似finally
4、切面
(1)把通知应用到切入点过程
1、Spring框架一般都是基于Aspect)实现AOP操作
(1)什么是Aspect
Aspect不是Spring组成部分,独立AOP框架,一般把Aspect]和Spirng框架一起使用,进行AOP操作
2、基于Aspect]实现AOP操作W
(1)基于xml配置文件实现
(2)基于注解方式实现(使用)
第一步:添加依赖spring-aspects-5.3.18.jar,在spring->lib里有
添加spring-aspects-5.3.18.jar的依赖
下载地址为:一共3个包,你直接在Download后面输入包名,去掉所有符号,并且小写.htm结尾。
http://www.java2s.com/Code/Jar/c/Downloadcomspringsourceorgaspectjweaver168releasejar.htm
第二步:学会切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构:
excution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))
举例1:对com.atquigu.dao.BookDao类里面的add进行增强
权限修饰符:*表示任意、返回类型:可以忽略、方法名称:用点与类路径连一块、参数列表:两个点表示所有参数
*execution(com.atguigu.dao.BookDao.add(…))
下面表示com.atguigu.dao下的所有类的所有方法作为切入点
execution(com.atguigu.dao. * .(…))
(0)新建XML文件,配置XML文件,引入了2个xmlns:context和xmlns:aop
(1)自定义类,用来被增强
package com.sj.computer.www.myspringdemo9;
import org.springframework.stereotype.Repository;
@Repository
public class testdao {
public void say(String name) {
System.out.println("我的名字是:" + name);
}
}
(2)自定义类,用来增强别人@Aspect
package com.sj.computer.www.myspringdemo9;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;
@Service
@Aspect
public class testservice {
//在这里写增强的方法
@Before(value = "execution(* com.sj.computer.www.myspringdemo9.testdao.say(..))")
public void say(){
System.out.println("在你之前执行!");
}
}
(3)自定义类,用来测试
package com.sj.computer.www.myspringdemo9;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
@Controller
public class testcontroller {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("com/sj/computer/www/myspringdemo9/demo9.xml");
testdao testdao =(testdao) context.getBean("testdao");
testdao.say("张三");
}
}
在你之前执行! 我的名字是:张三
注意事项:增强和被增强的类都要声明bean注解,因为aop过程底层用到了两者的对象
testservice类的2个注解
@Service
@Aspect
---等价于---
TestdaoImpl testdao = new TestdaoImpl();
Testdao obj = (Testdao) Proxy.newProxyInstance(Testdao.class.getClassLoader(), interfaces,new MyProxyImpl(testdao));
(4)5种通知
1、环绕通知@Around
@Around(value = "execution(* com.sj.computer.www.myspringdemo9.testdao.say(..))")
public void say(ProceedingJoinPoint point) throws Throwable {
System.out.println("在AROUNT前执行!");
point.proceed();
System.out.println("在AROUNT后执行!");
}
在你后面执行! 我的名字是:张三 在你后面执行!
2、前置通知
@Before(value = "execution(* com.sj.computer.www.myspringdemo9.testdao.say(..))")
public void say1(){
System.out.println("before前置通知!");
}
3、后置通知
@After(value = "execution(* com.sj.computer.www.myspringdemo9.testdao.say(..))")
public void say2(){
System.out.println("after后置通知!");
}
4、最终通知
@AfterReturning(value = "execution(* com.sj.computer.www.myspringdemo9.testdao.say(..))")
public void say3(){
System.out.println("afterreturning最终通知!");
}
5、异常通知
@AfterThrowing(value = "execution(* com.sj.computer.www.myspringdemo9.testdao.say(..))")
public void say4(){
System.out.println("afterThrowing异常通知!");
}
以上5种一起执行的结果如下:
在AROUNT前执行!
before前置通知!
我的名字是:张三
afterreturning最终通知!
after后置通知!
在AROUNT后执行!
afterreturning是在返回值之后执行、after是在方法后执行。
使用@Pointcut,将AOP表达式公共部分抽取出来
@Pointcut(value = "execution(* com.sj.computer.www.myspringdemo9.testdao.say(..))")
public void pointCut(){}
//在这里写增强的方法
@Around(value = "pointCut()")
public void say(ProceedingJoinPoint point) throws Throwable {
System.out.println("在AROUNT前执行!");
point.proceed();
System.out.println("在AROUNT后执行!");
}
设置多个增强类的优先级,在类上面使用@Order(数字),数字越小优先级越高