spring
Spring是一个java框架,java经典三层架构中都有它的身影。主要可以实现低耦合(IOC)和高内聚(AOP)。
-
-
声明式事务的支持
-
方便程序的测试
-
方便集成各种优秀框架
-
降低javaEE API的使用难度
Spring的体系结构
spring中的API: 控制反转 (IOC)``` 依赖注入 (DI) ``` 注解方式使用IOC ``` 代理设计模式``` springAOP
使用IOC开发需要使用导入jar包来进行注解开发等等... 这些是需要导入的jar包 jar包可以到GitHub开源网站上寻找并下载
- 控制反转(IOC): 所谓的IOC就是控制反转,简单来说就是将对象的创建和生命周期交给spring框架进行管理,从而在开发中不再需要关注对象的细节,而是在需要的时候由spring框架提供处理,这样的机制称之为控制反转。通过这种方式可以解耦
((创建对象实例的控制权反转),说的是一个类A要调用另一个类B,本来应该在类A里面创建B的实例的,控制权在A手里。现在用了Spring了,有了IOC,控制权就不在A手里了,而是交到Spring的IOC容器了,A要用到B,那Spring就把B分给A了)
使用spring开发时需要在src文件下面创建 applicationContext.xml文件 并且配置bean标签 开启包扫描
(创建spring反转IOC) 需要在src文件下面先创建一个 Spring Config文件
文件中创建bean标签 class属性是要放进spring容器中的哪个类(全路径,创建类时必须提供一个无参构造 ) id是class属性的唯一表示; 在测试类中创建
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applictionContext.xml");
在类路经下寻找 applictionContext.xml文件也就是Spring Config文件; 然后使用返回值去调用.getBean() 括号中写上applictionContext.xml 《bean》标签中的id,也就是spring容器中类(强转),就可以调用类中的方法属性。用返回值去调用.getBean() 括号中也可以使用spring容器中的(类名.class字节码对象\也可以传入《bean》标签中的id + 类名)
静态工厂创建对象: 在《bean》标签中创建静态static工厂方法需要添加一个 factory-method 意思就是要创建对象使用的方法
实体工厂创建对象:在《bean》标签中创建id(class的标识) class (类的全路径) 再创建《bean》标签 id是这个《bean》标签中class的标识 factory-bean里面是上个《bean》标签中的id factory-method中是实体工厂类中的方法,也就是需要两个《bean》标签。
spring自带工厂:在实体类中需要实现FactoryBean<> 接口,<>号中需要对象,然后实现了;两个方法 一个是getObject 工厂需要产生的对象 ; 一个是getObjectType 返回的对象的类型; 还以一个重写方法是默认不选择的,是isSingleton 返回的对象是否是单例的,返回true就表示是单例,返回false结果就是多例。jdk8之后已经在接口中实现了这个方法 返回结果是true 所以默认就是单例。
单例时要使用懒加载否则就会占用内存(在《bean》标签末尾添加lazy-init="true"),用户需要时在去创建;也可以设置全局懒加载,在文件约束的后面加上default-lazy-init="true", 全局变量和局部变量同时存在时,程序就会选择就近原则;
创建和销毁: 在《bean》标签中 添加init-method=“init”(初始化) destroy-method="release" (销毁) 在销毁关闭资源时需要使用向上造型 将 ApplicationContext(父类)替换成ClassPathXmlApplicationContext(子类),然后使用子类中close()方法。
在《bean》标签中创建末尾添加 scope = "prototype" 对象就像变为多例。
javaBean中单例和多例的区别(什么是单例多例:所谓单例就是所有的请求都用一个对象来处理,比如我们常用的service和dao层的对象通常都是单例的,而多例则指每个请求用一个新的对象来处理,比如action;
如何产生单例多例:在通用的SSH中,单例在spring中是默认的,如果要产生多例,则在配置文件的bean中添加scope=”prototype”;
为什么用单例多例:
1).之所以用单例,是因为没必要每个请求都新建一个对象,这样子既浪费CPU又浪费内存;
2).之所以用多例,是为了防止并发问题;即一个请求改变了对象的状态,此时对象又处理另一个请求,而之前请求对对象状态的改变导致了对象对另一个请求做了错误的处理;)
<context:component-scan base-package="com.xxxx"></context:component-scan>
Spring IoC 容器管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息创建。我们可以把 Spring IoC 容器看作是一个大工厂,Bean 相当于工厂的产品。如果希望这个大工厂生产和管理 Bean,就需要告诉容器需要哪些 Bean
通常情况下,Spring 的配置文件都是使用 XML 格式的。XML 配置文件的根元素是 <beans>,该元素包含了多个子元素 <bean>。每一个 <bean> 元素都定义了一个 Bean,并描述了该 Bean 是如何被装配到 Spring 容器中的。
在xml中配置<bean>标签时标签中的属性特别重要,会经常搞错属性值//下面是bean标签中的常用属性值
- 依赖注入 (DI)(对象创建中给其对象设置值就是依赖注入)
所谓的IOC就是控制反转,简单来说就是将对象的创建和生命周期交给spring框架进行管理,从而在开发中不再需要关注对象的细节,而是在需要的时候由spring框架提供处理,这样的机制称之为控制反转。而在创建对象的过程中spring可以以配置的方式对属性进行值的设置,这个过程称之为依赖注入(DI);依赖注入主流分为set方法注入和构造方法注入
spring内置可直接注入类型:
创建实体类 实体类中可是使用常用类型与自定义类型,必须提供set方法
在《bean》标签中根据类型的不同进行属性设置: ref为引用类型
- 自动装配: 也可以和全局懒加载一样,在约束下面使用defualt-autowire="byType"
autowire:取值有byName和byType
byName:默认根据javabean的属性名去容器中寻找相同id的对象,如果找到自动装配
byType:默认根据javabean的属性的类型去容器中寻找相同类型的对象,如果找到自动装配。如果找到多个类型,报错
假如在Teacher类中添加了Student类
那么在依赖注入时可以给两个同时赋值,打印时Teacher中就会包含Student的信息
- 构造方法注入:
前提条件是属性需要提供全参的构造方法
以上就是ioc开发使用
- 注解方式开发
- 在spring开启包扫描后在类中添加对应的类注解然后添加自动装配才能够实现高内聚与低耦合,也可以是实现面向接口的方式去实现
- 当开发时添加了过多的注解但是没有办法运行时就好好看看异常和调用关系,使用的API是否正确,程序是一个全面的调用关系
静态代理: 定义一个接口被代理者实现接口实现方法 代理者实现接口,增强方法 并且调用代理者的方法
使用注解开发时需要在配置文件中开启包扫描,(扫描报下的所有代码文件)
<!--开启包扫描-->
<context:component-scan base-package="com.javass"></context:component-scan>
注解 | 解释 |
---|---|
@Controller | 组合注解(组合了@Component注解),应用在MVC层(控制层),DispatcherServlet会自动扫描注解了此注解的类,然后将web请求映射到注解了@RequestMapping的方法上。 |
@Service | 组合注解(组合了@Component注解),应用在service层(业务逻辑层) |
@Reponsitory | 组合注解(组合了@Component注解),应用在dao层(数据访问层) |
@Component | 表示一个带注释的类是一个“组件”,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。 |
@Autowired | Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入。) |
@Resource | JSR-250提供的注解 |
@Inject | JSR-330提供的注解 |
@Configuration | 声明当前类是一个配置类(相当于一个Spring配置的xml文件) |
@ComponentScan | 自动扫描指定包下所有使用@Service,@Component,@Controller,@Repository的类并注册 |
@Bean | 注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。 |
@Aspect | 声明一个切面(就是说这是一个额外功能) |
@After | 后置建言(advice),在原方法前执行。 |
@Before | 前置建言(advice),在原方法后执行。 |
@Around | 环绕建言(advice),在原方法执行前执行,在原方法执行后再执行(@Around可以实现其他两种advice) |
@PointCut | 声明切点,即定义拦截规则,确定有哪些方法会被切入 |
@Transactional | 声明事务(一般默认配置即可满足要求,当然也可以自定义) |
@Cacheable | 声明数据缓存 |
@EnableAspectJAutoProxy | 开启Spring对AspectJ的支持 |
@Value | 值得注入。经常与Sping EL表达式语言一起使用,注入普通字符,系统属性,表达式运算结果,其他Bean的属性,文件内容,网址请求内容,配置文件属性值等等 |
@PropertySource | 指定文件地址。提供了一种方便的、声明性的机制,用于向Spring的环境添加PropertySource。与@configuration类一起使用。 |
@PostConstruct | 标注在方法上,该方法在构造函数执行完成之后执行。 |
@PreDestroy | 标注在方法上,该方法在对象销毁之前执行。 |
@Profile | 表示当一个或多个指定的文件是活动的时,一个组件是有资格注册的。使用@Profile注解类或者方法,达到在不同情况下选择实例化不同的Bean。@Profile(“dev”)表示为dev时实例化。 |
@EnableAsync | 开启异步任务支持。注解在配置类上。 |
@Async | 注解在方法上标示这是一个异步方法,在类上标示这个类所有的方法都是异步方法。 |
@EnableScheduling | 注解在配置类上,开启对计划任务的支持。 |
@Scheduled | 注解在方法上,声明该方法是计划任务。支持多种类型的计划任务:cron,fixDelay,fixRate |
@Conditional | 根据满足某一特定条件创建特定的Bean |
@Enable* | 通过简单的@Enable*来开启一项功能的支持。所有@Enable*注解都有一个@Import注解,@Import是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean(1.直接导入配置类2.依据条件选择配置类3.动态注册配置类) |
@RunWith | 这个是Junit的注解,springboot集成了junit。一般在测试类里使用:@RunWith(SpringJUnit4ClassRunner.class) — SpringJUnit4ClassRunner在JUnit环境下提供Sprng TestContext Framework的功能 |
@ContextConfiguration | 用来加载配置ApplicationContext,其中classes属性用来加载配置类:@ContextConfiguration(classes = {TestConfig.class(自定义的一个配置类)}) |
@ActiveProfiles | 用来声明活动的profile–@ActiveProfiles(“prod”(这个prod定义在配置类中)) |
@EnableWebMvc | 用在配置类上,开启SpringMvc的Mvc的一些默认配置:如ViewResolver,MessageConverter等。同时在自己定制SpringMvc的相关配置时需要做到两点:1.配置类继承WebMvcConfigurerAdapter类2.就是必须使用这个@EnableWebMvc注解。 |
@RequestMapping | 用来映射web请求(访问路径和参数),处理类和方法的。可以注解在类和方法上,注解在方法上的@RequestMapping路径会继承注解在类上的路径。同时支持Serlvet的request和response作为参数,也支持对request和response的媒体类型进行配置。其中有value(路径),produces(定义返回的媒体类型和字符集),method(指定请求方式)等属性。 |
@ResponseBody | 将返回值放在response体内。返回的是数据而不是页面 |
@RequestBody | 允许request的参数在request体中,而不是在直接链接在地址的后面。此注解放置在参数前。 |
@PathVariable | 放置在参数前,用来接受路径参数。 |
@RestController | 组合注解,组合了@Controller和@ResponseBody,当我们只开发一个和页面交互数据的控制层的时候可以使用此注解。 |
@ControllerAdvice | 用在类上,声明一个控制器建言,它也组合了@Component注解,会自动注册为Spring的Bean。 |
@ExceptionHandler | 用在方法上定义全局处理,通过他的value属性可以过滤拦截的条件:@ExceptionHandler(value=Exception.class)–表示拦截所有的Exception。 |
@ModelAttribute | 将键值对添加到全局,所有注解了@RequestMapping的方法可获得次键值对(就是在请求到达之前,往model里addAttribute一对name-value而已)。 |
@InitBinder | 通过@InitBinder注解定制WebDataBinder(用在方法上,方法有一个WebDataBinder作为参数,用WebDataBinder在方法内定制数据绑定,例如可以忽略request传过来的参数Id等)。 |
@WebAppConfiguration | 一般用在测试上,注解在类上,用来声明加载的ApplicationContext是一个WebApplicationContext。他的属性指定的是Web资源的位置,默认为src/main/webapp,我们可以修改为:@WebAppConfiguration(“src/main/resources”)。 |
@EnableAutoConfiguration | 此注释自动载入应用程序所需的所有Bean——这依赖于Spring Boot在类路径中的查找。该注解组合了@Import注解,@Import注解导入了EnableAutoCofigurationImportSelector类,它使用SpringFactoriesLoader.loaderFactoryNames方法来扫描具有META-INF/spring.factories文件的jar包。而spring.factories里声明了有哪些自动配置。 |
@SpingBootApplication | SpringBoot的核心注解,主要目的是开启自动配置。它也是一个组合注解,主要组合了@Configurer,@EnableAutoConfiguration(核心)和@ComponentScan。可以通过@SpringBootApplication(exclude={想要关闭的自动配置的类名.class})来关闭特定的自动配置。 |
@ImportResource | 虽然Spring提倡零配置,但是还是提供了对xml文件的支持,这个注解就是用来加载xml配置的。例:@ImportResource({“classpath |
@ConfigurationProperties | 将properties属性与一个Bean及其属性相关联,从而实现类型安全的配置。例:@ConfigurationProperties(prefix=”authot”,locations={“classpath |
@ConditionalOnBean | 条件注解。当容器里有指定Bean的条件下。 |
@ConditionalOnClass | 条件注解。当类路径下有指定的类的条件下。 |
@ConditionalOnExpression | 条件注解。基于SpEL表达式作为判断条件。 |
@ConditionalOnJava | 条件注解。基于JVM版本作为判断条件。 |
@ConditionalOnJndi | 条件注解。在JNDI存在的条件下查找指定的位置。 |
@ConditionalOnMissingBean | 条件注解。当容器里没有指定Bean的情况下。 |
@ConditionalOnMissingClass | 条件注解。当类路径下没有指定的类的情况下。 |
@ConditionalOnNotWebApplication | 条件注解。当前项目不是web项目的条件下。 |
@ConditionalOnResource | 条件注解。类路径是否有指定的值。 |
@ConditionalOnSingleCandidate | 条件注解。当指定Bean在容器中只有一个,后者虽然有多个但是指定首选的Bean。 |
@ConditionalOnWebApplication | 条件注解。当前项目是web项目的情况下。 |
@EnableConfigurationProperties | 注解在类上,声明开启属性注入,使用@Autowired注入。例:@EnableConfigurationProperties(HttpEncodingProperties.class)。 |
@AutoConfigureAfter | 在指定的自动配置类之后再配置。例:@AutoConfigureAfter(WebMvcAutoConfiguration.class) |
在希望被spring框架管理的类上面加上对应的注解,就相当于加入了spring容器中;
当一个组件代表数据访问层(DAO)的时候,我们使用@Repository进行注解
当一个组件代表业务层时,我们使用@Service进行注解
当一个组件代表业务层时,我们使用@Service进行注解
@Autowired:属于Spring 的org.springframework.beans.factory.annotation包下,可用于为类的属性、构造器、方法进行注值
@Resource:不属于spring的注解,而是来自于JSR-250位于java.annotation包下,使用该annotation为目标bean指定协作者Bean
@PostConstruct 和 @PreDestroy 方法 实现初始化和销毁bean之前进行的操作
常用的注解是 @Component @Controller用在web层 @Service用在 service层 @Repository 用在DAO层
如何使用注解开发时想要实现多例可以在类上面添加注解 @Scope("prototype")实现多例 实现懒加载在类上添加@Lazy注解 初始化方法添加@PostConstruct注解 销毁方法添加@PreDestroy注解
spring核心配置类 用于代替applicationContext.xml的作用 @Configuration 这个主机添加到类的上面 在类中的方法添加上@Bean 注解表示返回值的内容要存入spring容器中
那么测试类就不能使用AbstractXmlApplicationContext()方法来获取类的属性,要使用下面这行代码来实现(注解配置的AppliationContext)
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
动态代理:JDK自带的动态代理和第三方(cglib)的动态代理
jdk自带动态代理实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// JDK自带的动态代理 代理者是动态生成的 这个动态代理需要被代理者实现了接口
public class TestDemo2 {
public static void main(String[] args) {
// 匿名内部类本质上就是一个类的子类或者一个接口的实现类
// 只要一个类能够被继承,就能使用匿名内部类
Object obj = new Object(){
private int a;
private void eat(){
}
};
Fan fan = new Fan();
/**
*loader参数:用来生成代理者类的类加载器,一般都传入被代理者类的类加载器
*interfaces参数:用来确定生成代理者类需要实现的接口,一般都传入被代理者类实现的接口
* h参数:是一个接口,需要写一个类实现接口,实现回调函数的逻辑。
* 返回值就是动态生成的代理者
*/
Skill1 proxy = (Skill1) Proxy.newProxyInstance(Fan.class.getClassLoader(),
Fan.class.getInterfaces(),
new InvocationHandler() {
// 回调函数 当代理者调用接口中的方法时,会先调用这个函数
@Override
/**
* proxy :代理者
* method:需要执行的方法
* args:执行方法的参数
* Object:执行方法的返回值
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object obj;
if ("rap".equals(method.getName())){
System.out.println("检查权限");
System.out.println("打印日志");
obj = method.invoke(fan, args);
}else{
System.out.println("开启事务");
obj = method.invoke(fan,args);
}
return obj;
}
});
// 代理者调用方法
proxy.rap(10);
proxy.dance();
}
}
interface Skill1{
void rap(int a);
void dance();
}
// 被代理者
class Fan implements Skill1{
@Override
public void rap(int a) {
System.out.println("抢地盘,夹毛居,再大的场合都不得虚");
}
@Override
public void dance() {
System.out.println("跳舞~~");
}
}
CGLIB动态代理
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// CGLIB动态代理,不需要被代理者实现接口。它的实现方式是基于继承
public class TestDemo2 {
public static void main(String[] args) {
// 使用第三方的CGLIB动态生成代理
// 创建被代理者对象
DLRB dlrb = new DLRB();
// 创建生成动态代理的对象
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(dlrb.getClass().getClassLoader());
// 设置父类
enhancer.setSuperclass(dlrb.getClass());
// 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
/**
*
* @param o 代理
* @param method 方法
* @param objects 参数
* @param methodProxy
* @return 就是方法执行之后的返回值对象
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("检查权限");
System.out.println("打印日志");
// 使用被代理者对象调用方法
Object obj = method.invoke(dlrb, objects);
return obj;
}
});
// 生成代理者对象
DLRB proxy = (DLRB) enhancer.create();
proxy.rap();
proxy.dance();
}
}
// 被代理者 假设是地理热吧
class DLRB{
public void rap(){
System.out.println("地理热吧古力娜扎阿拉斯加");
}
public void dance(){
System.out.println("扭~");
}
}
以上就是IOC与DI待补充...
- springAOP
AOP称之为面向切面编程。是可以在层与层之间增加一个可编程的切面类,用来将功能性代码提取到切面中统一处理,有助于实现高内聚的特性。
AOP的作用
-
-
性能统计
-
权限控制
-
事务处理
- 异常处理
AOP的好处
-
-
高内聚
- 更好的系统扩展性
AOP的专业术语
-
连接点
-
层与层之间方法调用的过程称之为连接点
-
-
切入点表达式
-
用于筛选需要进行增强功能的方法的表达式
-
-
切入点
-
被切入点表达式筛选出来的需要增强的方法
-
-
切面
-
切入点要增强的代码所在的类
-
-
通知
-
-
目标对象
-
真正要调用的方法所在的类的对象
-
- 织入
- 织入是将增强添加到目标类具体连接点上的过程
使用AOP切面编程时需要导入mybatis.jar包,https://mvnrepository.com/网站直接搜索
切面小案例 切入点表达式分为粗粒度和细粒度within是粗粒度表达式
格式:within(包名.类名)
表示这个类中所有的连接点都会被表达式识别筛选,成为切入点。
-
情况二:
格式:within(包名.*) *代表通配符,匹配指定包下所有的类。
eg:within(com.shangma.service.impl.*) 表示impl包下所有的类(不包含子目录)
-
情况三
eg:
within(com.shangma.service.*.*)
表示service包下的所有的包下的所有的类(不包含子包)
-
情况四
eg:
within(com.shangma.service..*)
表示service包下的所有的包(包含子包)的所有的类
//切面类
//放入容器配置AOP
@Component
public class FirstAspect {
//通知
public void before() {
System.out.println("before代码执行了...");
}
}
在xml文件中配置AOP
<!-- 配置AOP-->
<aop:config>
<aop:pointcut id="pc00" expression="execution(void com.javasm.service.impl.UserServiceImpl.update*(com.javasm.entity.User))"/>
<!-- 配置切面-->
<aop:aspect ref="firstAspect">
<aop:before method="before" pointcut-ref=""></aop:before>
</aop:aspect>
</aop:config>
@Component
public class TestDemo {
@Autowired
private UserService userService;
@Test
public void registUser(User user){
userService.registUser(user);
}
语法:
execution(权限修饰符(省略) 返回值类型 包名.类名.方法名(参数类型,参数类型...) )
eg1:
execution(void com.shangma.service.UserServiceImpl.addUser(com.shangma.User))
精确到指定类的指定方法
eg2:
execution(void com.shangma.service.*.findUser(com.shangma.User))
表示service包下的所有的类的findUser方法,并且findUser方法需要一个类型为User的参数,返回值类型是void
eg3:
execution(void com.shangma.service.UserServiceImpl.find*())
UserServiceImpl类中所有以find开头的无参方法
eg4:
execution(void com.shangma.service.UserServiceImpl.find*(..))
UserServiceImpl类中所有find开头的方法(任意个数和类型的参数)
eg5
execution(* com.shangma.service..*.*(..))
解释:
第一个*:任意的返回值类型
..*:service包下的所有的类和子包下所有的类
.*:任意方法
.. :任意参数
3.8.2 前置通知
在目标方法执行之前执行的通知
前置通知可以选择性的接收JoinPoint参数,参数中有方法的所有信息
使用方式是直接在前置通知方法的参数中写上形参就可以获取到,系统会默认给形参赋值
环绕通知
在目标方法执行之前和之后执行。
环绕通知必须显式的调用目标方法,否则目标方法不会执行.
环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个null
后置通知
在目标方法成功执行完成之后执行的通知。
后置通知也可以选择性的接收JoinPoint参数,参数中有方法的信息。
可以获取目标方法结束后的返回值
注意点:JoinPoint参数必须在参数列表的第一位
最终通知
是在目标方法执行之后执行的通知。
和后置通知不同,后置通知中目标方法如果执行的时候出现异常,那么目标方法没有执行完,不会触发后置通知。
最终通知是目标方法无论是否执行完都会执行的通知。
异常通知
在目标方法抛出异常时执行的通知
后置通知也可以选择性的接收JoinPoint参数,参数中有方法的信息。
可以获取抛出的异常
注意点:JoinPoint参数必须在参数列表的第一位
AOP注解方式使用
需要现在配置文件中配置注解驱动
<aop:aspectj-autoproxy/>
然后在切面类上加上注解,切面注解
@Aspect
然后在切面类中的方法添加对应的注解
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//放入容器中
@Component
@Aspect //将当前类设置为切面类
public class SecondAspect {
@Pointcut("execution(* com.javasm.service..*.*(..))")
public void pc(){}
//前置通知
@Before("pc()")
public void before(JoinPoint joinPoint){
System.out.println("前置通知...");
}
//后置通知
@AfterReturning(value = "pc()",returning = "obj")
public void afterReturning(JoinPoint joinPoint,Object obj){
System.out.println("后置通知..." + obj);
}
//环绕通知
@Around("pc()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知执行前...");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕通知执行后...");
return proceed;
}
//最终通知
@After("pc()")
public void after(JoinPoint joinPoint){
System.out.println("最终通知...");
}
//异常通知
@AfterThrowing(value = "pc()",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常通知...");
}
}
待....
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?