您的位置:首页 > 编程语言 > Java开发

JavaWeb框架-【Spring】-2-让你我痴迷滴Spring!

2017-11-06 10:48 267 查看
话说:

各位读者早上好,上一篇博客初步了解了下Spring,这篇深入总结下。

Hibernate、Struts2现在主流用得少了,但是它们蕴含的思想还是蛮好的;Spring则非常强大,如春天般生机盎然。主流的SSM开发框架中的springMVC及MyBatis将在后续几篇博客中“粉墨登场”。到现在为止SSH的每个成员就介绍完毕了,下一篇博客将整合Spring+Struts2+Hibernate。

还是那句话,技术会过时,但是其中孕育的思想不会过时。

开发环境

IntelliJ IDEA(2017.2.5)

目录:

一、构造方法注入

二、自动装配

三、注解

四、设计模式

五、三种方式实现AOP

六、总结

一、构造方法注入

整体布局如下:



这次引入接口,通过面向接口编程的方式,来实现构造方法注入。类中以接口作为属性,然后配置bean,接着配置bean的属性。属性如果不注入,就会报错:NullPointExcepiton

前期准备:

新建IUserDao、UserDa
4000
o负责数据持久化;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[/code] 
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方式

通过配置切面、切入点即可。就这么简单!

好了,午安!各位读者,下期再会!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐