1、IOC概念
2、IOC底层体现了Java中什么原理。
3、spring中IOC创建的对象默认是?何时创建?
1、掌握AOP相关的概念
2、AOP底层动态代理的两种方式
3、Spring中AOP的实现
4、Spring中单元测试的支持
5、Spring中使用注解实现
日志,在程序中记录程序的运行过程的文件。
作用:
1、排错。
2、行为记录。行为分析,行为模拟,推荐等。(大数据)
项目中的使用:(管理日志)
日志的每一次的记录。
日志的输出类型(错误日志等)控制。
输出的位置(控制台、文件)控制。
使用日志框架。log4j的使用:
步骤:1、导入依赖
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
2、编写日志相关代码
public class TestLog {
// 根据当前类生成一个日志对象
private static Logger logger = Logger.getLogger(TestLog.class);
public void myDay(){
System.out.println("我的一天");
// 调试级别(一般情况只是在代码调试过程中才需要输出的内容)
logger.debug("洗漱");
logger.debug("吃早饭");
// 普通信息
logger.info("坐公交去上班");
// 警告信息
logger.warn("有人踩了我一脚");
logger.warn("我就瞪了他一眼");
logger.warn("他骂了我一句");
// 错误信息
logger.error("我给了他一巴掌");
logger.error("他不让我走");
// 普通信息
logger.info("到公司上班");
logger.info("上班一天过去了");
}
public static void main(String[] args) {
new TestLog().myDay();
}
}
3、配置日志管理
log4j.properties
# ROOTER
log4j.rootLogger=WARN,CONSOLE,FILE
# CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} %-5p %-20c %x %m%n
# FILE
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=D:/day56.log
log4j.appender.FILE.MaxBackupIndex=20
log4j.appender.FILE.MaxFileSize=10MB
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} %-5p %-20c %x %m%n
# ERROR
log4j.appender.ERR=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ERR.File=D:/day56Error.log
log4j.appender.ERR.Append=true
log4j.appender.ERR.Threshold=ERROR
log4j.appender.file.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.ERR.layout=org.apache.log4j.PatternLayout
log4j.appender.ERR.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
AOP:面向切面(Aspect方面)的编程 。将核心业务与横切业务(日志、权限、事务)在开发时分离开来,直到真正执行时才动态的组合(静态织入)在一起实现。
好处:解耦。
AOP是OOP的一个补充。可以看成一种设计模式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PBOtEZdk-1685175863808)(https://note.youdao.com/yws/public/resource/761be3201af0f3f2d4bc97f763ac08c5/xmlnote/E82EABB8B8664C35A653F73FD8D394ED/4457)]
1、动态代理
代理模式:
a、JDK自带的动态代理:需要接口
b、asm(cglib)动态代理:不需要接口
2、静态织入
示例:
核心业务:数据库操作ProductDAO、ProductDAOImpl
横切业务:日志操作MyLog
定义一个动态组合的规则:MyHandler
在MyHandler定义了ProductDAO在调用时,如何去切入MyLog的规则。
根据当前类的加载器,以及ProductDAO的接口,加上MyHanlder里面定义的规则,动态的创建了一个类(此处是$Proxy5),它实现了ProductDAO接口,并且将定义的规则也实现了,将其赋值给ProductDAO接口,并调用其方法(save),就动态的实现了动态代理的过程。
[要求:]
一定要有接口
// JDK中实现动态代理的关键接口
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
public interface ProductDAO {
void save();
void update();
}
public class ProductDAOImpl implements ProductDAO{
public void save() {
System.out.println("添加成功");
}
public void update() {
System.out.println("修改成功");
}
}
public class MyLog {
public void before(){
System.out.println("方法调用之前");
}
public void after(){
System.out.println("方法调用之后");
}
}
public class MyHandler implements InvocationHandler{
// 目标对象
private Object target;
private MyLog myLog = new MyLog();
public MyHandler(Object target){
this.target = target;
}
// 动态调用方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myLog.before();
Object re = method.invoke(target, args); // 真正执行目标对象的方法
myLog.after();
return re;
}
}
public class ProductDAOTest {
private ProductDAO productDAO;
@Before // 在测试方法执行之前
public void init(){
productDAO = new ProductDAOImpl(); // target
MyHandler handler = new MyHandler(productDAO); // 实现组装的规则
// 动态的创建一个代理对象来执行
productDAO = (ProductDAO) Proxy.newProxyInstance(
ProductDAO.class.getClassLoader(), // 得到productDAO对应的类加载器
productDAO.getClass().getInterfaces(), // 得到productDAO对应的接口
handler);// 组装规则的对象
System.out.println(productDAO.getClass());
}
@Test
public void testSave(){
productDAO.save();
}
@Test
public void testUpdate(){
productDAO.update();
}
}
示例:
核心业务:数据库操作StudentDAO
横切业务:日志操作MyLog1
定义一个动态组合的规则:MyCglibHandler
Cglib实现动态代理的原理:根据业务类,以及规则,动态生成一个业务类的子类,该子类中重组了该规则的实现,通过父类去引用子类的对象,来实现多态,以得到动态代理的效果。
[注意:]
在创建子类对象时,需要设置父类的类型和规则的对象。
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>4.3.18.RELEASEversion>
dependency>
public class StudentDAO {
public void delete(){
System.out.println("正在删除");
}
public void find(){
System.out.println("正在查询");
}
}
public class MyLog1 {
public void before(){
System.out.println("方法调用之前");
}
public void after(){
System.out.println("方法调用之后");
}
}
public class MyCglibHandler implements MethodInterceptor{
private MyLog1 log = new MyLog1();
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
// 调用横切的日志
log.before();
// 调用原本的父类业务操作
Object re = methodProxy.invokeSuper(o, objects);
log.after();
return re;
}
}
public class StudentDAOTest {
private StudentDAO studentDAO; // 父类
@Before
public void init(){
// studentDAO = new StudentDAO(); // 创建业务类的对象
MyCglibHandler handler = new MyCglibHandler(); // 创建规则对象
Enhancer enhancer = new Enhancer(); // 创建一个用来生产动态代理对象的对象
// 设置相应的内容
enhancer.setSuperclass(StudentDAO.class); // 设置父类的类型
enhancer.setCallback(handler); // 设置规则,回调
studentDAO = (StudentDAO) enhancer.create(); // 动态创建一个子类的对象
System.out.println(studentDAO.getClass());
}
@Test
public void testDelete(){
studentDAO.delete();
}
@Test
public void testFind(){
studentDAO.find();
}
}
步骤:
1、导入依赖
2、编写相应的业务和横切
3、编写spring的配置文件,配置aop过程
4、编写单元测试
导入依赖
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>4.3.18.RELEASEversion>
dependency>
dependencies>
业务和横切
public interface ProductDAO {
void save();
void update();
}
public class ProductDAOImpl implements ProductDAO {
@Override
public void save() {
System.out.println("添加成功");
}
@Override
public void update() {
System.out.println("修改成功");
}
}
public class StudentDAO {
public void delete(){
System.out.println("删除成功");
}
public void find(){
System.out.println("查找成功");
}
}
public class MyLog {
public void before(){
System.out.println("方法开始执行");
}
public void after(){
System.out.println("方法执行结束");
}
}
配置文件
<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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="productDAO" class="com.qf.day1.dao.impl.ProductDAOImpl">bean>
<bean id="studentDAO" class="com.qf.day1.dao.StudentDAO">bean>
<bean id="myLog" class="com.qf.day1.aop.MyLog">bean>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.qf.day1.dao.*.*(..))">aop:pointcut>
<aop:aspect ref="myLog">
<aop:before method="before" pointcut-ref="pc">aop:before>
<aop:after method="after" pointcut-ref="pc">aop:after>
aop:aspect>
aop:config>
beans>
单元测试
public class ProductDAOTest {
private ProductDAO productDAO;
private StudentDAO studentDAO;
@Before
public void init(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
productDAO = context.getBean("productDAO", ProductDAO.class);
studentDAO = context.getBean("studentDAO", StudentDAO.class);
}
@Test
public void testSave(){
productDAO.save();
studentDAO.delete();
}
}
before:前置通知,方法调用前调用
after:后置通知,方法调用后调用
after-throwing:抛出异常时通知
after-returning:返回值时通知
around:环绕通知。可以手动去实现代码组合的过程。(回到底层实现)
// 动态调用方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
int i = 5;
myLog.before();
Object re = method.invoke(target, args); // 真正执行目标对象的方法
myLog.after();
System.out.println(i);
return re;
}
连接点:当业务方法调用时,横切方法会切入,此时两者会在某个方法中同时被调用,此处会有一个连接点。可以通过连接点在横切方法中了解业务方法的特征。
public void before(JoinPoint jp){
System.out.println(jp.getTarget().getClass().getName() + "===" + jp.getSignature().getName());
System.out.println("方法开始执行");
}
在spring框架中也有对Junit的支持,步骤如下:
1、导入依赖
2、在单元测试类上写相应的注解
3、在使用的属性上加相应的注解
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>4.3.18.RELEASEversion>
<scope>testscope>
dependency>
@RunWith(SpringJUnit4ClassRunner.class) // spring对junit的支持
@ContextConfiguration("classpath:spring.xml") // 指定配置文件的名称
public class ProductServletTest {
@Autowired
private ProductServlet productServlet;
@Test
public void testSave(){
productServlet.save();
}
}
步骤:
1、导入相关的依赖
2、编写业务类和切面类
3、编写spring.xml中的配置(ioc的包扫描,以及aop的自动代理)
4、添加业务类中的ioc相关注解,以及切面类中的aop注解
5、编写单元测试
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>4.3.18.RELEASEversion>
dependency>
业务类和切面类
@Repository
public class StudentDAO {
public void save(){
System.out.println("添加成功");
}
}
/*
*/
@Component
@Aspect //
public class MyLog {
@Pointcut("execution(* com.qf.day2.dao.*.*(..))") //
public void pc(){}
@Before("pc()") //aop:before method="before" pointcut-ref="pc">
public void before(){
System.out.println("方法开始执行");
}
@After("pc()") //
public void after(){
System.out.println("方法执行结束");
}
}
spring.xml中的配置
<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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.qf.day2">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class StudentDAOTest {
@Autowired
private StudentDAO studentDAO;
@Test
public void testSave(){
studentDAO.save();
}
}
连接点(Joinpoint):
特定点是程序执行的某一个特定位置,如类开始初始化前,类初始化后,类某一个方法调用前/调用后,方法抛出异常后,一个类或一段程序代码拥有一些具有边界性质的特定点,这写代码中的特定点就称为"连接点",Spring仅支持方法连接点,即仅能在方法调用前,方法调用后,方法抛出异常时,以及方法调用前后这些程序执行点织入通知.
切点(Pointcut)
每个程序类都拥有对个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物,但在众多连接点中,如何定位某些感兴趣的连接点呢?AOP通过"切点"定位特定的连接点.
通知(Advice),增强
通知是织入目标类连接点上的一段程序代码,在Spring中,通知不仅可以描述程序代码,还拥有另一个和连接点相关的信息,这便是执行点的方位,结合执行点的方位信息和切点信息,就可以找到特定的连接,正因为通知既包含了用于添加到目标连接点上的一段执行逻辑,又包含用于定位连接点的方位信息,所以Spring所提供的通知接口都是带方位名的.如BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等.BeforeAdvice表示方法调用前的位置.而AfterReturningAdivce表示访问返回后的位置,所以只有结合切点和通知,才能确定特定的连接点并实施通知逻辑.
目标对象(Target)
通知逻辑的织入目标类.如果没有AOP,那么目标业务类需要自己实现所有逻辑,如果使用AOP可以把一些非逻辑性代码通过AOP织入到主程序代码上.
引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法.这样,即使一个业务类原本没有实现某一个接口,通过AOP的引介功能,也可以动态地为该业务类添加接口的实现逻辑.让业务类成为这个接口的实现类.
织入(Weaving)
织入是将通知添加到目标类具体链接点上的过程,AOP就像一台织布机,将目标类,通知,或者引介天衣无缝的编织到一起,我们不能不说"织入"这个词太精辟了,根据不同的实现技术,AOP有3种织入方式:
编译期织入,这要求使用特殊的Java编译器.
类装载期织入,这要求使用特殊的类装载器.
动态代理织入,在运行期为目标类添加通知生成子类的方式.
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载器织入.
代理(Proxy)
一个类被AOP织入通知后,就产生了一个结果类.它是融合了原类和通知逻辑的代理类,根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以可以采用与调用原类相同的方法调用代理类.
切面(Aspect)
切面由切点和通知(介入)组成,它既包括横切逻辑的定义,也包括链接点的定义,也包括链接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切所定义的横切逻辑织入切面所指定的链接点中.
AOP的工作重心在于如何将通知应用于目标对象的连接点中上,这里包含两项工作:
第一,如何通过切点和通知定位到链接点上;
第二,如何在通知中编写切面代码;
写一个MyBatis框架的基本操作代码。
加入Spring整合的步骤:
1、导入相关依赖
2、编写spring的配置文件
3、修改代理以使用spring的IOC的注解
4、编写单元测试
5、编写web.xml配置文件
6、在servlet中使用WebApplicationContextUtils来操作context。
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.40version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.6version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.2version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.2version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.qf.day2">context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties">context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.user}">property>
<property name="password" value="${jdbc.password}">property>
<property name="jdbcUrl" value="${jdbc.url}">property>
<property name="driverClass" value="${jdbc.driver}">property>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">property>
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml">property>
bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.qf.day2.dao">property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">property>
bean>
beans>
web.xml配置
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>examdisplay-name>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
web-app>
LoginServlet
@WebServlet("/login.do")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final String username = req.getParameter("username");
final String pwd = req.getParameter("pwd");
final WebApplicationContext context = WebApplicationContextUtils
.getWebApplicationContext(req.getServletContext());
final UserService userService = context.getBean("userService", UserService.class);
final Users users = userService.login(username, pwd);
if (users == null){
req.setAttribute("msg", "用户名或密码错误");
req.getRequestDispatcher("index.jsp").forward(req, resp);
}else{
req.getSession().setAttribute("user", users);
resp.sendRedirect("leave.do");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
整合log4j
1、导入log4j的依赖
2、编写log4j.properties文件
3、编写mybatisCfg.xml,并将其在spring.xml中引用
mybatisCfg.xml
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="log4j"/>
settings>
configuration>
spring.xml
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">property>
<property name="mapperLocations" value="classpath:mapper/*Mapper.xml">property>
<property name="configLocation" value="classpath:mybatisCfg.xml">property>
bean>