话说:
各位读者早上好,上一篇博客初步了解了下Spring,这篇深入总结下。
Hibernate、Struts2现在主流用得少了,但是它们蕴含的思想还是蛮好的;Spring则非常强大,如春天般生机盎然。主流的SSM开发框架中的springMVC及MyBatis将在后续几篇博客中“粉墨登场”。到现在为止SSH的每个成员就介绍完毕了,下一篇博客将整合Spring+Struts2+Hibernate。
还是那句话,技术会过时,但是其中孕育的思想不会过时。
开发环境
IntelliJ IDEA(2017.2.5)
目录:
一、构造方法注入
二、自动装配
三、注解
四、设计模式
五、三种方式实现AOP
六、总结
一、构造方法注入
整体布局如下:
这次引入接口,通过面向接口编程的方式,来实现构造方法注入。类中以接口作为属性,然后配置bean,接着配置bean的属性。属性如果不注入,就会报错:NullPointExcepiton
前期准备:
新建IUserDao、UserDao负责数据持久化;IUserService、UserService业务逻辑处理。
IUserDao
package com.hmc.spring.dao;
public interface IUserDao {
void add();
}
UserDao
package com.hmc.spring.dao;
/**
* User:Meice
* 2017/11/4
*/
public class UserJdbcDao implements IUserDao {
public void add() {
System.out.println("使用JDBC方式保存用户....");
}
}
IUserService
package com.hmc.spring.service;
public interface IUserService {
void save();
}
UserService
package com.hmc.spring.service;
import com.hmc.spring.dao.IUserDao;
/**
* User:Meice
* 2017/11/4
*/
public class UserService implements IUserService {
// private IUserDao userJdbcDao;
private IUserDao userDao;
/*public IUserDao getUserJdbcDao() {
return userJdbcDao;
}
public void setUserJdbcDao(IUserDao userJdbcDao) {
this.userJdbcDao = userJdbcDao;
}*/
//最好一同也写一个无参的,避免忘记带参构造方法属性注入时报错:No default constructor found;
public UserService() {}
//构造方法初始化
public UserService(IUserDao userDao) {
this.userDao = userDao;
}
public void save() {
//这里面要调用JDBC的add方法
userDao.add();
//自动装配autowire="byName"
//userJdbcDao.add();
}
/* public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
*/
}
我们的目的是在UserService里面要调用IUserDao中的方法,本质就是调用UserJdbcDao中的方法。不过这里属性我们定义为接口,而不是直接是对应Dao层类。什么用呢?方便扩展。下次再增加IBookDao、BookDao就很方便。
上一篇博客我们是怎么注入的?我们定义了属性IUserDao,然后生成get(),set()方法,在spring.xml中配置UserService的bean,然后给这个bean配置property,属性名字要和set()后的setYY 中的yy保持一致。
这里要实现的目标是,通过构造方法属性注入,就不用生成set()了。如何实现构造方法注入?
第一步:UserService中生成构造方法
package com.hmc.spring.service;
import com.hmc.spring.dao.IUserDao;
/**
* User:Meice
* 2017/11/4
*/
public class UserService implements IUserService {
private IUserDao userDao;
//最好一同也写一个无参的,避免忘记带参构造方法属性注入时报错:No default constructor found;
public UserService() {}
//构造方法初始化
public UserService(IUserDao userDao) {
this.userDao = userDao;
}
public void save() {
//这里面要调用JDBC的add方法
userDao.add();
//自动装配autowire="byName"
//userJdbcDao.add();
}
}
第二步:spring.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--注入userJdbcDao-->
<bean name="userJdbcDao" class="sdcom.hmc.spring.dao.UserJdbcDao"></bean>
<!-- <bean name="userHibernateDao" class="com.hmc.spring.dao.UserHibernateDao"></bean>-->
<!--注入userService-->
<bean name="userService" class="com.hmc.spring.service.UserService" autowire="constructor">
<!--byName表示根据bean的名字来自动装配,所以引用的时候,属性名要和bean名字一致并生成get(),set()
而且,给哪个bean加上自动装配,表示对其引用属性进行自动装配,而不是其本身-->
<!--属性注入-->
<!--<property name="userDao" ref="userJdbcDao"></property>-->
<!--构造方法注入-->
<constructor-arg index="0" ref="userJdbcDao"></constructor-arg>
</bean>
</beans>
无非就是换一种方式注入属性而已。需要注意的是,这里如果没有配置,就会报错:NullPointException,因为找不到具体属性值呢。
二、自动装配
自动装配就是通过一个属性Autowired来配置属性,替换之前需要配置
property这个属性节点。
主要类型:no、 byName、byType、constructor
1) byName
顾名思义,byName,属性名不可以乱取;定义属性名字要和要引用的bean一致。
第一步:定义属性并生成get() set()
private IUserDao userJdbcDao;
public IUserDao getUserJdbcDao() {
return userJdbcDao;
}
public void setUserJdbcDao(IUserDao userJdbcDao) {
this.userJdbcDao = userJdbcDao;
}
第二步:自动装配 加上autowire=”byName”
<bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean>
<bean name="userService" class="com.hmc.spring.service.UserService" autowire="byType">
//这里神马也不用写奥~~~
</bean>
注意:这里因为是byName,定义的属性userJdbcDao要和bean的name一致,否则就报错NullPointExcep,找不到嘛!
2) byType
顾名思义,根据类型。什么类型,你定义的属性,要注入的属性的类型。所以名字随便取。
第一步:定义接口属性,并生成get set
private IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
第二步:自动装配 加入:autowire=”byType”
<bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean>
<bean name="userService" class="com.hmc.spring.service.UserService" autowire="byType">
//这里神马也不用写
</bean>
But!
弊端:如果这个接口有多个类实现,就会找不到具体调用哪个类的方法了。会报错:
java.lang.ExceptionInInitializerError
Caused by: ....NoUniqueBeanDefinitionException: No qualifying bean of type 'com.hmc.spring.dao.IUserDao' available: expected single matching bean but found 2: userJdbcDao,userHibernateDao
所以,谨慎使用。用的时候,属性就不要定义为接口了,直接定义为对应的实现类。
3) constructor
构造方法,顾名思义,之前的构造方法属性注入也可以通过autowire=”constructor”来配置
第一步:定义接口类型属性和构造方法
private IUserDao userDao;
public UserService() {}
//构造方法初始化
public UserService(IUserDao userDao) {
this.userDao = userDao;
}
第二步:加入自动装配constructor
第二步:加入自动装配constructor
<bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean>
<!--注入userService-->
<bean name="userService" class="com.hmc.spring.service.UserService" autowire="constructor">
//这里神马也不用写!
</bean>
But!
和byType一样,如果有多个类实现同一个接口,那么系统就不知道该调用实现类的方法
就会报错:java.lang.NullPointerException;比如以下两个bean都实现了IUserDao这个接口的add()方法
<bean name="userJdbcDao" class="com.hmc.spring.dao.UserJdbcDao"></bean>
<bean name="userHibernateDao" class="com.hmc.spring.dao.UserHibernateDao"></bean>
<!--注入userService-->
<bean name="userService" class="com.hmc.spring.service.UserService" autowire="constructor">
</bean>
三、注解
什么用呢?之前我们每次都要在spring.xml中配置bean,就代表类实例化了,这样有木有觉得很麻烦?是的,确实如此。怎么简化呢?注解。注解大家很熟悉,Junit中的@Test从此解放了main方法,测试起来灰常方便了呢。
1)主要几个注解@Repository @Service @Controller @Autowired @Qualifier 等
如何实现注解呢?
(1)开启注解支持
官方文档在spring.xml中加入头部信息
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
(2) 扫描加入注解的包
<context:component-scan base-package="com.hmc.spring"/>
(3)给相应业务类加上相应注解
Dao层
@Repository
public class UserDao implements IUserDao {}
Service层
@Service
public class UserService implements IUserService {}
Autowired实现自动装配
@Autowired(默认byType)
private IUserDao userDao;
可以使用Qualfier指定bean的名称
@Autowired
@Qualifier(value = "userJdbcDao")——手动指定在哪个bean进行装配
private IUserDao userDao;
如果生成了get() set()方法,就通过@Resource方法注解,等同于@Autowired + @Qualifier
private IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
@Resource(name = "userHibernateDao")//生成set()方法,通过Resource( name=""指定属性)
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
注意:@Service 和@Repository默认就是类名首字母小写,如果重命名的话,引用的时候,也要用引用的名字,否则报错:
No bean named ‘userJdbcDao’ available
2)自定义注解
我们不能总做使用者吧?如果可能,我们也要尽可能去创造。
1、新建一个注解类
package com.hmc.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD}) //表示在元素上运行
@Retention(RetentionPolicy.RUNTIME) //注解在运行时有效
public @interface Log_meice {
String value() default "";
}
2、加入元注解
@Target({ElementType.METHOD}) //表示在元素上运行
@Retention(RetentionPolicy.RUNTIME) //注解在运行时有效
3、测试
package com.hmc.spring.dao;
import com.hmc.spring.annotation.Log_meice;
public interface IUserDao {
@Log_meice("增加了用户")
void add();
@Log_meice("更新了用户")
void update();
@Log_meice("删除了用户")
void del();
void list();
}
四、设计模式
就是把所有的代码结构化分层,每一层职责明确。不论项目多么复杂,项目结构清晰明了。Model层
就是JavaBean,业务类的实体类;Dao层负责数据持久化,和数据库接入;Service层调用Dao层方法,实现
复杂的业务逻辑处理,Controller层主要做三件事:处理页面请求并接收参数、调用Service层方法、页面跳转。
各层层级关系明朗:Controller==>Service==>Dao 上层依赖于下层,下层为上层提供服务。
其实《金字塔结构》这本书讲解的是平时说话做事的逻辑层级;在代码世界里,本质也差不多。
五、三种方式实现AOP
1)经典方式:静态代理与动态代理
2)注解方式
3)XML配置方式
AOP——Aspect-Oriented Programming 面向切面编程
OC原则(open close);在不改编源码的情况下,为原代码织入新功能。
底层依赖的JDK的反射机制。反射我们不陌生了,之前JDBC一些列中,查的方法最后用到了Field.
1)经典方式:静态代理与动态代理
a、静态代理如何实现?
b、动态代理?
目的:UserService在调用Dao层add()方法时,为其增加日志信息。日志信息内容如下:
package com.hmc.spring.log;
import java.util.Date;
/**
* User:Meice
* 2017/11/5
*/
public class Logger {
public static void log(String info) {
System.out.println(new Date()+"--->"+info);
}
}
a、静态代理如何实现?
package com.hmc.spring.dao;
import com.hmc.spring.log.Logger;
import org.springframework.stereotype.Repository;
/**
* User:Meice
* 2017/11/5
*/
@Repository
public class UserProxyJdbcDao implements IUserDao {
/**
* 静态代理步骤:
* 1、创建一个和要添加功能一模一样的Dao
* 2、加入新增方法
* 3、注解(自动装配)
*/
public void add() {
System.out.println("使用JDBC新增了用户....");
Logger.log("添加了用户");
}
public void update() {
}
public void del() {
}
public void list() {
}
}
b、动态代理?
先单纯实现动态代理,不与spring整合。
package com.hmc.spring.proxy;
import com.hmc.spring.dao.IUserDao;
import com.hmc.spring.dao.UserJdbcDao;
import com.hmc.spring.log.Logger;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* User:Meice
* 2017/11/5
*/
public class LoggerProxy implements InvocationHandler {
//代理对象-->代言人
private Object target;
public LoggerProxy(Object o) {//被代理对象->产品
this.target = o;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("嗯哈");
// Logger.log("Before:进行了相关操作");
Object obj = method.invoke(target,args);
// Logger.log("After:进行了相关操作");
return obj;
}
public static void main(String[] args) {
UserJdbcDao userJdbcDao = new UserJdbcDao();//被代理对象
LoggerProxy proxy = new LoggerProxy(userJdbcDao); //得到代理对象
/* IUserDao userDaoProxy =(IUserDao)Proxy.newProxyInstance(UserJdbcDao.class.getClassLoader(),IUserDao.class.getInterfaces(),proxy);
userDaoProxy.add();*/
/**
* 运行结果:
* java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.hmc.spring.dao.IUserDao
*/
IUserDao ud =(IUserDao)Proxy.newProxyInstance(UserJdbcDao.class.getClassLoader(),UserJdbcDao.class.getInterfaces(),proxy);
ud.add();
/**
* 以上整体执行流程是这样的:
* ud.add(),ud表示指定的接口(IUserDao)的代理实例,,该接口将add()方法的调用指派到指定的调用处理程序-proxy
* 调用处理程序就是指实现InvocationHandler接口的这个类,也就是这里的LoggerProxy,只要把它实例化——通过带参构造
* 方法把被代理对象-UserJdbcDao传过去即可。
*
* 最终,你看到的输出信息,就是这个类中invoke()方法体重输出的信息,而不是原有UserJdbcDao中的方法。
* 因为方法已经通过Method的invoke方法从底层映射过来了。
*
* 再说简单点:也就是通过2步走就把UserJdbcDao中的add()方法复制了一份到这个调用处理程序中。然后在这里
* 加入我们需要增加的内容,而不改变原有方法。
*
* 第一步:创建一个类,实现InvocationHandler接口,并实现方法。(这个类就是调用处理程序)
* 第二步:通过Proxy这个类实例化该调用处理程序,实例化过程中就制定了代理产品-UserJdbcDao
*
*
*/
}
}
通过spring.xml来实现动态代理
首先编写一个类,类似上面的。
package com.hmc.spring.proxy;
import com.hmc.spring.annotation.Log_meice;
import com.hmc.spring.dao.IUserDao;
import com.hmc.spring.log.Logger;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* User:Meice
* 2017/11/5
*/
public class LoggerProxySpring implements InvocationHandler{
//制定代理人
private Object target;
/* public LoggerProxySpring(Object o) {
this.target = o;
Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),this);
}*/
/**
* 构造方法无法返回Proxy实例化后被代理对象实现的接口
就会报错: No qualifying bean of type 'com.hmc.spring.dao.IUserDao' available: expected at least 1 bean which
qualifies as autowire candidate. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired
(required=true), @org.springframework.beans.factory.annotation.
Qualifier(value=loggerProxySpring)}
表示希望返回IuserDao,实际却是loggerProxySpring这个实例
所以构造方法无法处理Proxy实例化这一步
*/
//构造方法无法实例化Proxy所返回的被代理对象所实现的接口,所以要这么写
public static Object getInstance(Object o) {
LoggerProxySpring proxy = new LoggerProxySpring();
proxy.target = o;
return Proxy.newProxyInstance(o.getClass().getClassLoader(),o.getClass().getInterfaces(),proxy);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//对带有指定参数args的指定对象target调用由此 Method 对象表示的底层方法
//如果方法是list,不用打印日志信息
/* if (!"list".equals(method.getName()) || !"search".equals(method.getName())){
Logger.log("Before:进行了相关操作");
}*/
//注解 ——对加了注解的方法输出日志
Log_meice lo = method.getAnnotation(Log_meice.class);
if(lo != null) {
Logger.log("Before:进行了操作:"+lo.value());
}
Object obj = method.invoke(target,args);
Logger.log("After:进行了相关操作");
return obj;
}
}
配置spring.xml
<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.hmc.spring"/>
<!--配置动态代理-->
<bean name="loggerProxySpring" class="com.hmc.spring.proxy.LoggerProxySpring" factory-method="getInstance" >
<constructor-arg value="userJdbcDao"></constructor-arg>
</bean>
<!--配置BookDao的动态代理-->
<bean name="bookProxySpring" class="com.hmc.spring.proxy.LoggerProxySpring" factory-method="getInstance">
<constructor-arg value="bookDao"></constructor-arg>
</bean>
</beans>
弊端:无法实现指定方法输出日志。
一般,查询方法不加日志,但是增修、修改、删除加日志,查询方法不加日志。如何处理?
做个判断:
if ("list".equals(method.getName())){
Logger.log("Before:进行了相关操作");
}
如果方法名不一致呢?加入多个判断?
//对带有指定参数args的指定对象target调用由此 Method 对象表示的底层方法
if (!"list".equals(method.getName()) || !"search".equals(method.getName())){
Logger.log("Before:进行了相关操作");
}
你能穷尽所有查的方法么?只要名字一变化,就不一样了…所以注解最方便:
2)注解方式实现AOP
@AspectJ-简化配置
有木有觉!得上面的动态代理很麻烦,比较复杂?其实笔者测试的时候,不甚顺利。注解方式实现AOP真的是So Easy!
(1)导入包aspectj包
只有导入这个包,才能用@AspectJ这个注解,不然没有呢
<!--导入AspectJWeaver包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.12</version>
</dependency>
(2)开启spring AOP支持
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
要想开启支持,就需要有这个标签可以用,头部只有加入spring-aop,才会有下面的标签。
<!--开启spring AOP 支持-->
<aop:aspectj-autoproxy/>
(3)定义切面
@Component
@Aspect
public class LoggerProxy {
}
在代理类头部只加上@Aspect是不够的,还需要加上@Component
* @Aspect表明这是个切面类,里面会定义切入点。
@Component表明这个类在bean中所处的位置
* 因为它不属于Repository\Service\Controller
(4)定义通知
/**
* 前置通知
*表达式匹配规则:
* 第一个*:方法的修饰符(方法的返回类型)
* 第二个*:service包下面的任意类
* 第三个*:service包下面的任意类的任意方法
* ..*表示service包下面的包的任意类
*
*/
@Before("execution(* com.hmc.spring.service.*.add*(..)) || " +
"execution(* com.hmc.spring.service.*.del*(..)) ")
public void BeforeMethod() {
Logger.log("<<< 在调用方法前执行");
}
/**
* 后置通知
*/
@After("execution(* com.hmc.spring.service.*.*(..))")
public void AfterMethod() {
Logger.log("在方法之后调用>>>");
//Logger.log("<<<环绕通知>>>");
}
/**
* 环绕方法
* 光加@Around是不行的,不会环绕
*/
@Around("execution(* com.hmc.spring.service.*.*(..))")
public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
Logger.log("<<<方法前后都会执行>>>");
pjp.proceed();
Logger.log("<<<方法前后都会执行>>>");
}
/**
* 测试异常执行通知
* AfterReturnning
* 只有方法正常执行,才会调用
*/
@AfterReturning("execution(* com.hmc.spring.service.*.*(..))")
public void afterReturnning() {
Logger.log("方法抛出异常之后才执行");
}
/**
* AfterThrowing表明
* 方法只有出现异常,才能看到这个日志
*/
@AfterThrowing("execution(* com.hmc.spring.service.*.*(..))")
public void afterThrowing() {
Logger.log("方法抛出异常才能看到我");
}
//连接点Joinpoint
可以更加精细化输出调用方法名等信息
@After("execution(* com.hmc.spring.service.*.*(..))")
public void AfterMethod(JoinPoint jp) {
System.out.println("正在执行的方法是:"+jp.getSignature().getName());
Logger.log("在方法之后调用>>>");
//Logger.log("<<<环绕通知>>>");
}
/**
* JoinPoint 可以输出执行方法的名称等详细信息
* org.aspectj.lang.JoinPoint;
*
* 运行结果:
* 十一月 06, 2017 12:36:40 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring.xml]
正在执行的方法是:save
Mon Nov 06 00:36:41 CST 2017 在方法之后调用>>>
java.lang.NullPointerException
*
*
* 有什么用?根据这个可以快捷找到哪个方法执行异常
* 以及其他更加精细化操作
*/
3)XML配置方式实现AOP
注解有注解的好处,但是过多注解,也让人不知所云,而且注解最大弊端就是侵入性,直接改源代码去了,这时候配置就出场了。大型项目很多用配置,便于修改,结构清晰。
1、开启AOP支持
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
2、spring.xml中配置
<!--为了可以使用注解,加入context头部信息并配置注解-->
<context:component-scan base-package="com.hmc.spring"/>
<!-- XML配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="loggerProxy">
<!--配置切入点-->
<aop:pointcut id="allMethod" expression="execution(* com.hmc.spring.service.*.*(..))" />
<!--<aop:before method="BeforeMethod" pointcut-ref="allMethod"/>-->
<aop:after method="AfterMethod" pointcut-ref="allMethod"/>
</aop:aspect>
</aop:config>
测试:
package com.hmc.spring;
import com.hmc.spring.service.IUserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* User:Meice
* 2017/11/6
*/
public class SpringTest {
private static ApplicationContext ac;
static {
ac = new ClassPathXmlApplicationContext("spring.xml");
}
@Test
public void test() {
IUserService ius = ac.getBean("userService", IUserService.class);
ius.save();
}
}
运行结果:
十一月 06, 2017 8:44:15 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring.xml]
使用JDBC增加用户...
Mon Nov 06 08:44:17 CST 2017 方法之后调用》》》
六、总结
1、构造方法注入,通过constructor-org来注入;
2、自动装配与注解。
在spring.xml头部加入context==》扫描加入注解的包==》主要有:@Component 通用的,还是有用的,比如Proxy那个类,就要加上。@Repository注解Dao层,
@Service注解Service层;@Controller层, @Autowired自动装配(byName byType);@Qualifier(value =”“)表明具体应用的类
非常方便,从此不用再配置。注解一旦加上,就代表已经实例化。
3、设计模式
就是把所有的代码结构化分层,每一层职责明确。不论项目多么复杂,项目结构清晰明了。Model层
就是JavaBean,业务类的实体类;Dao层负责数据持久化,和数据库接入;Service层调用Dao层方法,实现
复杂的业务逻辑处理,Controller层主要做三件事:处理页面请求并接收参数、调用Service层方法、页面跳转。
各层层级关系明朗:Controller==>Service==>Dao 上层依赖于下层,下层为上层提供服务。
4、自定义注解
新建一个@Annotation类==》设置默认值==》通过元注解定义注解具体作用域==》搞定!
5、AOP实现3中方式
1)代理模式
(1)静态代理-类似复制一个类,然后加入想要织入的功能;
(2)动态代理—本质还是复制了这个类,只是通过这几个步骤实现:新建一个类,实现接口InvocationHandler,重写方法;定义代理人和代理产品,通过构造方法赋值产品; ==>method.invoke实现方法的复制==》Proxy.newInstance实例话这个代理类,返回一个接口。
2)注解方式
spring.xml中加入头文件aop==》开启注解支持==》创建代理类,@Aspect定义切面;@Before等定义切入点及通知==》测试运行
3)XML方式
通过配置切面、切入点即可。就这么简单!
好了,午安!各位读者,下期再会!