@
目录SSM框架是目前常用的框架,那框架是什么勒,简单来说,框架就是别人已经写好的软件,已经写好了模板,简化我们开发。后续我们如果需要进行其他开发,只需要进行一些增加删除。
SSM框架分别为:
Spring:service处理框架,主要进行创建对象以及进行一些业务处理
SpringMVC:主要进行一些请求的处理
Mybatis:进行数据库的处理,JDBC的加强
Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发
的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可
以在 Java SE/EE 中使用的轻量级开源框架。
- 轻量
- 解耦合
- 支持AOP
- 容易集成其他框架
这里看看就好,混个眼熟,现在可能看不懂,到时候深入了解了可以在回来瞅一瞅
控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代
码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对
象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,
依赖的管理。
简单来说:之前我们创建一个对象是通过new来创建的。例如:创建一个Data对象 new Data(),但是现在我们在框架中不需要自己来创建对象,框架的容器会自动来实现对象的创建和赋值,容器是啥,可以把他看成一个装对象的东东。
其中IOC是利用DI(Dependency Injection)依赖注入实现的。
依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建
被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
首先创建一个maven项目,选择模板quickstart,其实也可以不选模板,创建一个空项目,但是选择模板的好处是能为我们创建相应的文件夹,如果不选模板,那我们就自己创建相应的目录
<!--Spring 依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.5.RELEASE</version> </dependency>
接口:
public interface SomeService { public void doSome(); }
实体类
public class SomeServiceImpl implements SomeService { public void doSome() { System.out.println("dosome"); } }
在resources目录下新建配置文件,配置文件一般取名applicationContext.xml
id:对象的自定义名称,唯一值。 spring通过这个名称找到对象
name:对象的自定义名称,可以有多个值。
class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类), spring能创建一个非自定义类的对象吗, 创建一个存在的某个类的对象。
<bean name="doSomeService1,doSomeService2" class="com.springtest.service.impl.SomeServiceImpl"/> <bean id="doSomeService3" class="com.springtest.service.impl.SomeServiceImpl"/> <bean id="mydate" class="java.util.Date" />
public class MyTest { @Test public void test1() { String config = "applicationContext.xml"; ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(config); SomeService doSomeService = (SomeService) classPathXmlApplicationContext.getBean("doSomeService1"); doSomeService.doSome(); } }
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。
初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类:set 注入、构造注入。
set注入(设值注入) :spring调用类的set方法, 你可以在set方法中完成属性赋值
<bean id="xxx" class="yyy"> <property name="属性名称" value="属性值" /> </bean>
ref的值必须为某 bean 的 id 值。
<bean id="xxx" class="yyy"> <property name="属性名称" ref="bean的id(对象的名称)" /> </bean>
public class Student { private String name; private int id; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", id=" + id + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
<bean id="student" class="domain.Student"> <property name="id" value="1"/> <property name="name" value="张三"/> <!--set注入,如果属性为对象,使用ref=""进行赋值--> </bean>
public class MyTest { @Test public void test(){ String config = "applicationContext.xml"; ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(config); Student st = (Student) classPathXmlApplicationContext.getBean("student"); System.out.println(st.toString()); } }
set注入使用property进行赋值操作时需要保证实体类有set方法
构造注入:spring调用类有参数构造方法,在创建对象的同时,在构造方法中给属性赋值。 构造注入使用 <constructor-arg> 标签 <constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数。 <constructor-arg> 标签属性: name:表示构造方法的形参名 index:表示构造方法的参数的位置,参数从左往右位置是 0 , 1 ,2的顺序 value:构造方法的形参类型是简单类型的,使用value ref:构造方法的形参类型是引用类型的,使用ref
引用类型的自动注入: spring框架根据某些规则可以给引用类型赋值。·不用你在给引用类型赋值了 使用的规则常用的是byName, byType. 1.byName(按名称注入) : java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样,且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。 语法: <bean id="xx" class="yyy" autowire="byName"> 简单类型属性赋值 </bean> 2.byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性 是同源关系的,这样的bean能够赋值给引用类型 同源就是一类的意思: 1.java类中引用类型的数据类型和bean的class的值是一样的。 2.java类中引用类型的数据类型和bean的class的值父子类关系的。 3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的 语法: <bean id="xx" class="yyy" autowire="byType"> 简单类型属性赋值 </bean>
注意:在byType中, 在xml配置文件中声明bean只能有一个符合条件的,多余一个是错误的
包含关系的配置文件: spring-total表示主配置文件 : 包含其他的配置文件的,主配置文件一般是不定义对象的。 语法:<import resource="其他配置文件的路径" /> 关键字:"classpath:" 表示类路径(class文件所在的目录), 在spring的配置文件中要指定其他文件的位置, 需要使用classpath,告诉spring到哪去加载读取文件。 例如单独加载各个配置文件: <import resource="classpath:ba06/spring-school.xml" /> <import resource="classpath:ba06/spring-student.xml" /> 使用通配符加载: 在包含关系的配置文件中,可以通配符(*:表示任意字符) 注意: 主的配置文件名称不能包含在通配符的范围内(不能叫做spring-total.xml) 例如: <import resource="classpath:ba06/spring-*.xml" />
@Component: 创建对象的, 等同于<bean>的功能 属性:value 就是对象的名称,也就是bean的id值, value的值是唯一的,创建的对象在整个spring容器中就一个 位置:在类的上面 注意:Component不指定对象名称,由spring提供默认名称: 类名的首字母小写 @Component(value = "myStudent")等同于 <bean id="myStudent" class="com.test.ba01.Student" /> spring中和@Component功能一致,创建对象的注解还有: 1.@Repository(用在持久层类的上面) : 放在dao的实现类上面, 表示创建dao对象,dao对象是能访问数据库的。 2.@Service(用在业务层类的上面):放在service的实现类上面, 创建service对象,service对象是做业务处理,可以有事务等功能的。 3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的, 控制器对象,能够接受用户提交的参数,显示请求的处理结果。 以上三个注解的使用语法和@Component一样的。 都能创建对象,但是这三个注解还有额外的功能。 @Repository,@Service,@Controller是给项目的对象分层的。
声明组件扫描器(component-scan),组件就是java对象 base-package:指定注解(PS:创建对象相关注解)在你的项目中的包名。 component-scan工作方式: spring会扫描遍历base-package指定的包,把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。 加入了component-scan标签,配置文件的变化: 1.加入一个新的约束文件spring-context.xsd 2.给这个新的约束文件起个命名空间的名称 指定多个包的三种方式: <!--第一种方式:使用多次组件扫描器,指定不同的包--> <context:component-scan base-package="com.test.ba01"/> <context:component-scan base-package="com.test.ba02"/> <!--第二种方式:使用分隔符(;或,)分隔多个包名--> <context:component-scan base-package="com.test.ba01;com.test.ba02" /> <!--第三种方式:指定父包--> <context:component-scan base-package="com.test" />
例子:实体类Student
//使用Component创建对象为myStudent的Student对象 @Component("myStudent") public class Student { private String name; private Integer age; public Student() { System.out.println("==student无参数构造方法==="); } public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
<context:component-scan base-package="com.test.ba01" />
public class MyTest01 { @Test public void test01(){ String config="applicationContext.xml"; ApplicationContext ctx = new ClassPathXmlApplicationContext(config); //从容器中获取对象 Student student = (Student) ctx.getBean("myStudent"); System.out.println("student="+student); } }
属性: value 是String类型的,表示简单类型的属性值
位置:
1.在属性定义的上面,无需set方法,推荐使用。
2.在set方法的上面
如果使用外部属性配置文件的值,那就需要在Spring配置文件中添加一个加载属性配置文件。
<context:property-placeholder location="classpath:test.properties" />
@Component("myStudent") public class Student { /** * @Value: 简单类型的属性赋值 * 属性: value 是String类型的,表示简单类型的属性值 * 位置: 1.在属性定义的上面,无需set方法,推荐使用。 * 2.在set方法的上面 */ //@Value("李四" ) @Value("${myname}") //使用属性配置文件中的数据 private String name; @Value("${myage}") //使用属性配置文件中的数据 private Integer age; public Student() { System.out.println("==student无参数构造方法==="); } public void setName(String name) { this.name = name; } //@Value("30") public void setAge(Integer age) { System.out.println("setAge:"+age); this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
@Autowired: spring框架提供的注解,实现引用类型的赋值。 spring中通过注解给引用类型赋值,使用的是自动注入原理 ,支持byName, byType @Autowired:默认使用的是byType自动注入。 位置: 1)在属性定义的上面,无需set方法, 推荐使用 2)在set方法的上面
如果要使用byName方式,需要做的是: 1.在属性上面加入@Autowired 2.在属性上面加入@Qualifier(value="bean的id") :表示使用指定名称的bean完成赋值。
属性:required ,是一个boolean类型的,默认true required=true:表示引用类型赋值失败,程序报错,并终止执行。 required=false:引用类型如果赋值失败, 程序正常执行,引用类型是null
引用类型 @Resource: 来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值 使用的也是自动注入原理,支持byName, byType .默认是byName 位置: 1.在属性定义的上面,无需set方法,推荐使用。 2.在set方法的上面 注意:默认是byName: 先使用byName自动注入,如果byName赋值失败,再使用byType @Resource只使用byName方式,需要增加一个属性 name name的值是bean的id(名称) 例如: //只使用byName,不会使用byType @Resource(name = "mySchool") private School school;
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代码示例:
public interface SomeService { void doSome(); }
public class SomeServiceImpl implements SomeService { @Override public void doSome() { System.out.println("执行业务方法doSome"); } }
public class ServiceTools { public static void doLog(){ System.out.println("方法的执行时间:"+ new Date()); } public static void doTrans(){ //方法的最后,提交事务 System.out.println("方法执行完毕后,提交事务"); } }
public class MyIncationHandler implements InvocationHandler { //目标对象 private Object target; //SomeServiceImpl类 public MyIncationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //通过代理对象执行方法时,会调用执行这个invoke() Object res = null; ServiceTools.doLog(); //在目标方法之前,输出时间 //执行目标类的方法,通过Method类实现 res = method.invoke(target, args); //SomeServiceImpl.doSome() ServiceTools.doTrans(); //在目标方法执行之后,提交事务 //目标方法的执行结果 return res; } }
public class MyApp { public static void main(String[] args) { //使用jdk的Proxy创建代理对象 //1.创建目标对象 SomeService target = new SomeServiceImpl(); //2.创建InvocationHandler对象 InvocationHandler handler = new MyIncationHandler(target); //3.使用Proxy创建代理 SomeService proxy = (SomeService) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(),handler); //通过代理执行方法,会调用handler中的invoke() proxy.doSome(); } }
AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程
序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB
的动态代理。
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 Spring 框架中的一个重要内容。利用 AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向
切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,
而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框
架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
<!--aspectj依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.5.RELEASE</version> </dependency>
切面泛指交叉业务逻辑。例如事务处理、日志处理就可以理解为切面。常用的切面
是通知(Advice)。实际就是对主业务逻辑的一种增强。
简单来说:切面为要新添加的功能,要增强的功能
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不
能被增强的。
AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是: execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?) 解释: modifiers-pattern] 访问权限类型 ret-type-pattern 返回值类型 declaring-type-pattern 包名类名 name-pattern(param-pattern) 方法名(参数类型和参数个数) throws-pattern 抛出异常类型 ?表示可选的部分
简单来说切入点表达式子表示:execution(访问权限 方法返回值 方法声明(参数) 异常类型)
其中着重符号标注的方法返回值和方法声明(参数)这些都是必须要有的
符号 | 含义 |
---|---|
* | 0至多个任意字符 |
. . | 在方法参数中,表示任意多参数。用在包名中,表示当前包及其子包 |
+ | 用在类名后面,表示当前类和其子类。用在接口中,表示当前接口以及其实现类 |
目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。
通知表示切面的执行时间,Advice 也叫增强。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时间。
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参
数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、
目标对象等。
不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该
参数。
/** * @Aspect : 是aspectj框架中的注解。 * 作用:表示当前类是切面类。 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码 * 位置:在类定义的上面 */ @Aspect public class MyAspect { /** * 定义方法,方法是实现切面功能的。 * 方法的定义要求: * 1.公共方法 public * 2.方法没有返回值 * 3.方法名称自定义 * 4.方法可以有参数,也可以没有参数。 * 如果有参数,参数不是自定义的,有几个参数类型可以使用。 */ /** * @Before: 前置通知注解 * 属性:value ,是切入点表达式,表示切面的功能执行的位置。 * 位置:在方法的上面 * 特点: * 1.在目标方法之前先执行的 * 2.不会改变目标方法的执行结果 * 3.不会影响目标方法的执行。 */ /** * 指定通知方法中的参数 : JoinPoint * JoinPoint:业务方法,要加入切面功能的业务方法 * 作用是:可以在通知方法中获取方法执行时的信息, 例如方法名称,方法的实参。 * 如果你的切面功能中需要用到方法的信息,就加入JoinPoint. * 这个JoinPoint参数的值是由框架赋予, 必须是第一个位置的参数 */ @Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))") public void myBefore(JoinPoint jp){ //获取方法的完整定义 System.out.println("方法的签名(定义)="+jp.getSignature()); System.out.println("方法的名称="+jp.getSignature().getName()); //获取方法的实参 Object args [] = jp.getArgs(); for (Object arg:args){ System.out.println("参数="+arg); } } }
在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
/** * @Aspect : 是aspectj框架中的注解。 * 作用:表示当前类是切面类。 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码 * 位置:在类定义的上面 */ @Aspect public class MyAspect { /** * 后置通知定义方法,方法是实现切面功能的。 * 方法的定义要求: * 1.公共方法 public * 2.方法没有返回值 * 3.方法名称自定义 * 4.方法有参数的,推荐是Object ,参数名自定义 */ /** * @AfterReturning:后置通知 * 属性:1.value 切入点表达式 * 2.returning 自定义的变量,表示目标方法的返回值的。 * 自定义变量名必须和通知方法的形参名一样。 * 位置:在方法定义的上面 * 特点: * 1。在目标方法之后执行的。 * 2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能 * Object res = doOther(); * 3. 可以修改这个返回值 * * 后置通知的执行 * Object res = doOther(); * 参数传递: 传值, 传引用 * myAfterReturing(res); * System.out.println("res="+res) * */ @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))", returning = "res") public void myAfterReturing( JoinPoint jp ,Object res ){ // Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理 System.out.println("后置通知:方法的定义"+ jp.getSignature()); System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res); } }
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并
且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个
proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法
的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
@Aspect public class MyAspect { /** * 环绕通知方法的定义格式 * 1.public * 2.必须有一个返回值,推荐使用Object * 3.方法名称自定义 * 4.方法有参数,固定的参数 ProceedingJoinPoint */ /** * @Around: 环绕通知 * 属性:value 切入点表达式 * 位置:在方法的定义什么 * 特点: * 1.它是功能最强的通知 * 2.在目标方法的前和后都能增强功能。 * 3.控制目标方法是否被调用执行 * 4.修改原来的目标方法的执行结果。 影响最后的调用结果 * * 环绕通知,等同于jdk动态代理的,InvocationHandler接口 * * 参数: ProceedingJoinPoint 就等同于 Method * 作用:执行目标方法的 * 返回值: 就是目标方法的执行结果,可以被修改。 * * 环绕通知: 经常做事务, 在目标方法之前开启事务,执行目标方法, 在目标方法之后提交事务 */ @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { String name = ""; //获取第一个参数值 Object args [] = pjp.getArgs(); if( args!= null && args.length > 1){ Object arg= args[0]; name =(String)arg; } //实现环绕通知 Object result = null; System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date()); //1.目标方法调用 if( "zhangsan".equals(name)){ //符合条件,调用目标方法 result = pjp.proceed(); //method.invoke(); Object result = doFirst(); } System.out.println("环绕通知:在目标方法之后,提交事务"); //2.在目标方法的前或者后加入功能 //修改目标方法的执行结果, 影响方法最后的调用结果 if( result != null){ result = "Hello AspectJ AOP"; } //返回目标方法的执行结果 return result; } }
在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。
当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的
名称,表示发生的异常对象。
@Aspect public class MyAspect { /** * 异常通知方法的定义格式 * 1.public * 2.没有返回值 * 3.方法名称自定义 * 4.方法有个一个Exception, 如果还有是JoinPoint, */ /** * @AfterThrowing:异常通知 * 属性:1. value 切入点表达式 * 2. throwinng 自定义的变量,表示目标方法抛出的异常对象。 * 变量名必须和方法的参数名一样 * 特点: * 1. 在目标方法抛出异常时执行的 * 2. 可以做异常的监控程序, 监控目标方法执行时是不是有异常。 * 如果有异常,可以发送邮件,短信进行通知 * * 执行就是: * try{ * SomeServiceImpl.doSecond(..) * }catch(Exception e){ * myAfterThrowing(e); * } */ @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))", throwing = "ex") public void myAfterThrowing(Exception ex) { System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage()); //发送邮件,短信,通知开发人员 } }
无论目标方法是否抛出异常,该增强均会被执行。
@Aspect public class MyAspect { /** * 最终通知方法的定义格式 * 1.public * 2.没有返回值 * 3.方法名称自定义 * 4.方法没有参数, 如果还有是JoinPoint, */ /** * @After :最终通知 * 属性: value 切入点表达式 * 位置: 在方法的上面 * 特点: * 1.总是会执行 * 2.在目标方法之后执行的 * * try{ * SomeServiceImpl.doThird(..) * }catch(Exception e){ * * }finally{ * myAfter() * } * */ @After(value = "execution(* *..SomeServiceImpl.doThird(..))") public void myAfter(){ System.out.println("执行最终通知,总是会被执行的代码"); //一般做资源清除工作的。 } }
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。
其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
@Aspect public class MyAspect { @After(value = "mypt()") public void myAfter(){ System.out.println("执行最终通知,总是会被执行的代码"); //一般做资源清除工作的。 } @Before(value = "mypt()") public void myBefore(){ System.out.println("前置通知,在目标方法之前先执行的"); } /** * @Pointcut: 定义和管理切入点, 如果你的项目中有多个切入点表达式是重复的,可以复用的。 * 可以使用@Pointcut * 属性:value 切入点表达式 * 位置:在自定义的方法上面 * 特点: * 当使用@Pointcut定义在一个方法的上面 ,此时这个方法的名称就是切入点表达式的别名。 * 其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了 */ @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" ) private void mypt(){ //无需代码, }
<!--spring依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.5.RELEASE</version> </dependency> <!--aspectj依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.2.5.RELEASE</version> </dependency>
接口:
public interface SomeService { void doSome(String name,Integer age); }
实现类:
//目标类 public class SomeServiceImpl implements SomeService { @Override public void doSome(String name,Integer age) { //给doSome方法增加一个功能,在doSome()执行之前, 输出方法的执行时间 System.out.println("====目标方法doSome()===="); } }
@Aspect public class MyAspect { @Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))") public void myBefore(JoinPoint jp) //就是你切面要执行的功能代码 } }
在spring配置文件中配置:
<!--把对象交给spring容器,由spring容器统一创建,管理对象--> <!--声明目标对象--> <bean id="someService" class="com.test.ba01.SomeServiceImpl" /> <!--声明切面类对象--> <bean id="myAspect" class="com.test.ba01.MyAspect" />
在spring配置文件中配置:
<!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。 创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 创建为代理对象 所以目标对象就是被修改后的代理对象. aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。 --> <aop:aspectj-autoproxy />