AOP (Aspect Oriented Program,面向切面编程)把业务功能分为核心、非核心两部分。
在Spring的面向切面编程(AOP)思想里,非核心业务功能被定义为切面。核心业务功能和切面功能先被分别进行独立开发,然后把切面功能和核心业务功能“编织"在一起,这就是AOP
AOP将那 些与业务无关,却为业务模块所共同调用的逻辑封装起来,以便减少系统的重复代码,降低模块间的耦合度,利于未来的拓展和维护。这正是AOP的目的,它是Spring最为重要的功能之一,被广泛使用。
package com.itheima.domain;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
@Aspect
//使之成为切面类
@Component
//把切面类加入Ioc容器中
public class AopLog {
private Logger logger = LoggerFactory.getLogger(this.getClass());
ThreadLocal<Long> startTime = new ThreadLocal<Long>();
//线程局部变量,用于解决多线程中相同变量的访问冲突问题
@Pointcut("execution(public * com.itheima.controller.AopLogController.avoid())")
//定义切点
public void aopWebLog(){
System.out.println("切点");
}
@Before("aopWebLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable{
startTime.set(System.currentTimeMillis());
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//接收到请求,记录请求内容
HttpServletRequest request = attributes.getRequest();
logger.info("URL: " + request.getRequestURI().toString());
logger.info("HTTP方法:"+request.getMethod());
logger.info("IP方法:"+request.getRemoteAddr());
logger.info("类的方法:"+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
logger.info("参数:"+request.getQueryString());
//记录下请求内容
}
@AfterReturning(pointcut = "aopWebLog()",returning = "retObject")
public void doAfterReturning(Object retObject) throws Throwable {
logger.info("应答值:"+retObject);
logger.info("费时:"+(System.currentTimeMillis()-startTime.get()));
}
@AfterThrowing(pointcut = "aopWebLog()",throwing = "ex")
public void addAfterThrowingLogger(JoinPoint joinPoint,Exception ex){
logger.error("执行:"+"异常:",ex);
}
}
代码解读:
Pointcut定义时,还可以使用&&、||、! 这三个运算。进行逻辑运算。可以把各种条件组合起来使用
此类提供线程局部变量。这些变量不同于它们的正常对应变量,因为每个访问一个(通过它的get或set方法)的线程都有它自己的、独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程相关联的类中的私有静态字段(例如,用户 ID 或事务 ID)。
例如,下面的类生成每个线程本地的唯一标识符。线程的 id 在第一次调用ThreadId.get()时被分配,并且在后续调用中保持不变。
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
例子:
public class ThreadLocalTest02 {
public static void main(String[] args) {
ThreadLocal<String> local = new ThreadLocal<>();
IntStream.range(0, 10).forEach(i -> new Thread(() -> {
local.set(Thread.currentThread().getName() + ":" + i);
System.out.println("线程:" + Thread.currentThread().getName() + ",local:" + local.get());
}).start());
}
}
输出结果:
线程:Thread-0,local:Thread-0:0
线程:Thread-1,local:Thread-1:1
线程:Thread-2,local:Thread-2:2
线程:Thread-3,local:Thread-3:3
线程:Thread-4,local:Thread-4:4
线程:Thread-5,local:Thread-5:5
线程:Thread-6,local:Thread-6:6
线程:Thread-7,local:Thread-7:7
线程:Thread-8,local:Thread-8:8
线程:Thread-9,local:Thread-9:9
从结果可以看到,每一个线程都有自己的local 值,这就是TheadLocal的基本使用 。
只要线程处于活动状态并且ThreadLocal实例可访问,每个线程都持有对其线程局部变量副本的隐式引用;在线程消失后,它的所有线程本地实例副本都将受到垃圾回收(除非存在对这些副本的其他引用)
Session会话管理。提供对连接点可用状态和有关它的静态信息的反射访问。此信息可使用特殊形式thisJoinPoint从建议正文中获得。此反射信息的主要用途是跟踪和记录应用程序。
joinPoint.getSignature():获取当前切点方法,其值为String com.itheima.controller.AopLogController.avoid()
joinPoint.getSignature().getDeclaringTypeName():获取切点方法所在类的类型,这相当于调用 getDeclaringType().getName(),但会缓存结果以提高效率。其值为:com.itheima.controller.AopLogController
joinPoint.getSignature().getName():此签名的标识符部分。对于方法,这将返回方法名称,其值为:avoid()
RequestAttributes:用于访问与请求关联的属性对象的抽象。支持访问请求范围的属性以及会话范围的属性,具有“全局会话”的可选概念。可以为任何类型的请求/会话机制实现,特别是 servlet 请求。
ServletRequestAttributes为RequestAttributes接口的基于 Servlet 的实现。从 servlet 请求和 HTTP 会话范围访问对象,“会话”和“全局会话”之间没有区别。

ServletRequest:定义一个对象以向 servlet 提供客户端请求信息。 servlet 容器创建一个ServletRequest对象并将其作为参数传递给 servlet 的service方法。ServletRequest对象提供的数据包括参数名称和值、属性和输入流。扩展ServletRequest的接口可以提供额外的特定于协议的数据(例如,HTTP 数据由javax.servlet.http.HttpServletRequest提供。
HttpServletRequest:扩展ServletRequest接口以提供 HTTP servlet 的请求信息。servlet 容器创建一个HttpServletRequest对象并将其作为参数传递给 servlet 的服务方法( doGet 、 doPost等)。
类似于使用@Before注解可以修饰Before增强处理,使用@AfterReturning可修饰AfterReturning增强处理,AfterReturning增强处理将在目标方法正常完成后被织入。
使用@AfterReturning注解可指定如下两个常用属性。
1) pointcut/value:这两个属性的作用是一样的,它们都属于指定切入点对应的切入表达式。一样既可以是已有的切入点,也可直接定义切入点表达式。当指定了pointcut属性值后,value属性值将会被覆盖。
2) returning:该属性指定一个形参名,用于表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法的返回值。除此之外,在Advice方法中定义该形参(代表目标方法的返回值)时指定的类型,会限制目标方法必须返回指定类型的值或没有返回值。
使用@AfterThrowing注解可以修饰AfterThrowing增强处理,AfterThrowing增强处理主要用于处理程序中未处理的异常。使用@AfterThrowing注解时可指定如下的常用属性:
1) pointcut/value:这两个属性的作用是一样的,它们都用于指定该切入点对应的切入表达式。一样既可是一个已有的切入点,也可以直接定义切入点表达式。当指定了pointcut属性后,value属性值将会被覆盖。
2) throwing:该属性指定一个形参名,用于表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。除此之外,在Advice方法中定义该参数时,指定的类型,会限制方法必须抛出指定类型的异常。
@Before:在切入点开始处切入内容。
@After:在切入点结尾处切入内容。
@Around:在切入点前后切入内容,并控制何时执行切入点自身的内容。
@AfterThrowing:用来处理当切入内容部分抛出异常之后的处理逻辑。
@Aspect:标记为切面类。
package com.itheima.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AopLogController {
@GetMapping(value = "/aoptest")
public String avoid(){
Logger logger = LoggerFactory.getLogger(this.getClass());
logger.info("切点");
return "hello aop";
}
}
看下访问后控制台打印信息
2022-07-16 17:21:49.785 INFO 1412 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-07-16 17:21:49.785 INFO 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2022-07-16 17:21:49.786 TRACE 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@4886333
2022-07-16 17:21:49.786 TRACE 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@7451a4c0
2022-07-16 17:21:49.786 TRACE 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.theme.FixedThemeResolver@4f5dd299
2022-07-16 17:21:49.787 TRACE 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected DefaultRequestToViewNameTranslator
2022-07-16 17:21:49.787 TRACE 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Detected SessionFlashMapManager
2022-07-16 17:21:49.787 DEBUG 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2022-07-16 17:21:49.787 INFO 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2022-07-16 17:21:49.805 TRACE 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : GET "/aoptest", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2022-07-16 17:21:49.807 TRACE 1412 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.itheima.controller.AopLogController#avoid()
2022-07-16 17:21:49.813 TRACE 1412 --- [nio-8080-exec-1] o.s.web.method.HandlerMethod : Arguments: []
2022-07-16 17:21:49.815 INFO 1412 --- [nio-8080-exec-1] com.itheima.domain.AopLog : URL: /aoptest
2022-07-16 17:21:49.815 INFO 1412 --- [nio-8080-exec-1] com.itheima.domain.AopLog : HTTP方法:GET
2022-07-16 17:21:49.815 INFO 1412 --- [nio-8080-exec-1] com.itheima.domain.AopLog : IP方法:0:0:0:0:0:0:0:1
2022-07-16 17:21:49.817 INFO 1412 --- [nio-8080-exec-1] com.itheima.domain.AopLog : 类的方法:com.itheima.controller.AopLogController.avoid
2022-07-16 17:21:49.817 INFO 1412 --- [nio-8080-exec-1] com.itheima.domain.AopLog : 参数:null
2022-07-16 17:21:49.824 INFO 1412 --- [nio-8080-exec-1] com.itheima.controller.AopLogController : 切点
2022-07-16 17:21:49.824 INFO 1412 --- [nio-8080-exec-1] com.itheima.domain.AopLog : 应答值:hello aop
2022-07-16 17:21:49.824 INFO 1412 --- [nio-8080-exec-1] com.itheima.domain.AopLog : 费时:9
2022-07-16 17:21:49.833 DEBUG 1412 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Using 'text/html', given [text/html, application/xhtml+xml, image/avif, image/webp, application/xml;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2022-07-16 17:21:49.834 TRACE 1412 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Writing ["hello aop"]
2022-07-16 17:21:49.841 TRACE 1412 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerAdapter : Applying default cacheSeconds=-1
2022-07-16 17:21:49.841 TRACE 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned.
2022-07-16 17:21:49.841 DEBUG 1412 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK, headers={masked}
2022-07-16 17:21:49.907 TRACE 1412 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : GET "/favicon.ico", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2022-07-16 17:21:49.909 TRACE 1412 --- [nio-8080-exec-2] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to HandlerExecutionChain with [ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]] and 3 interceptors
2022-07-16 17:21:49.911 DEBUG 1412 --- [nio-8080-exec-2] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2022-07-16 17:21:49.911 TRACE 1412 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned.
2022-07-16 17:21:49.911 DEBUG 1412 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND, headers={masked}
2022-07-16 17:21:49.913 TRACE 1412 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2022-07-16 17:21:49.914 TRACE 1412 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : 2 matching mappings: [{ [/error]}, { [/error], produces [text/html]}]
2022-07-16 17:21:49.914 TRACE 1412 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2022-07-16 17:21:49.916 TRACE 1412 --- [nio-8080-exec-2] o.s.web.method.HandlerMethod : Arguments: [org.apache.catalina.core.ApplicationHttpRequest@349cdda4]
2022-07-16 17:21:49.925 DEBUG 1412 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [image/avif, image/webp, */*] and supported [application/json, application/*+json, application/json, application/*+json]
2022-07-16 17:21:49.925 TRACE 1412 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{timestamp=Sat Jul 16 17:21:49 CST 2022, status=404, error=Not Found, message=No message available, path=/favicon.ico}]
2022-07-16 17:21:49.958 TRACE 1412 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerAdapter : Applying default cacheSeconds=-1
2022-07-16 17:21:49.958 TRACE 1412 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned.
2022-07-16 17:21:49.958 DEBUG 1412 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404, headers={masked} 几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。
我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一
使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案
我正在开发一个xcode自动构建系统。在执行一些预构建验证时,我想检查指定的证书文件是否已被撤销。我了解securityverify-cert验证其他证书属性但不验证吊销。我如何检查撤销?我正在用Ruby编写构建系统,但我对任何语言的想法都持开放态度。我阅读了这个答案(Openssl-Howtocheckifacertificateisrevokedornot),但指向底部的链接(DoesOpenSSLautomaticallyhandleCRLs(CertificateRevocationLists)now?)进入的Material对我的目的来说有点过于复杂(用户上传已撤销的证书是一
关闭。这个问题是off-topic.它目前不接受答案。想改进这个问题吗?Updatethequestion所以它是on-topic用于堆栈溢出。关闭11年前。Improvethisquestion我不经常使用ruby-通常它加起来相当于每两个月或更长时间编写一次脚本。我的大部分编程都是使用C++进行的,这与ruby有很大不同。由于我与ruby之间的差距如此之大,我总是忘记语言的基本方面(比如解析文本文件和其他简单的东西)。我想每天练习一些基本的东西,我想知道是否有一些我可以订阅的网站,并且会向我发送当天的Ruby问题或类似的东西。有人知道这样的站点/Internet服务吗?
我一直在寻找一种以编程方式或通过命令行将mp3转换为aac的方法,但没有成功。理想情况下,我有一段代码可以从我的Rails应用程序中调用,将mp3转换为aac。我安装了ffmpeg和libfaac,并能够使用以下命令创建aac文件:ffmpeg-itest.mp3-acodeclibfaac-ab163840dest.aac当我将输出文件的名称更改为dest.m4a时,它无法在iTunes中播放。谢谢! 最佳答案 FFmpeg提供AAC编码功能(如果您已编译它们)。如果您使用的是Windows,则可以从here获取完整的二进制文件。
我想输入一个字符串并返回一个可用于描述字符串结构的正则表达式。正则表达式将用于查找更多与第一个结构相同的字符串。这是故意模棱两可的,因为我肯定会漏掉SO社区中的某个人会发现的情况。请发布任何和所有可能的方法来做到这一点。 最佳答案 简单的答案(可能不是您想要的)是:返回输入字符串(正则表达式特殊字符转义)。这始终是与字符串匹配的正则表达式。如果您希望识别某些结构,则必须提供有关您希望识别的结构类型的更多信息。如果没有这些信息,问题就会以模棱两可的方式陈述,并且有许多可能的解决方案。例如,输入字符串'aba'可以描述为'阿巴''阿巴*