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

java中设计模式在springmvc框架中的应用理解

2019-09-20 11:21 183 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_37582804/article/details/101054820

1简单.工厂模式
又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。
简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。
spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。如下配置

加载数据库配置 又例如quartz定时的应用中,所用到的配置online-reate-quartz-context.xml 中
<!-- 区域设备在线率定时任务处理类 设备在线率按照天统计 -->
<bean id="deviceOnlineRateAreaTaskQuartzDay" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass"
value="com.netbox.manageStatisticNew.job.DeviceOnlineRateAreaTask" />
<property name="jobDataAsMap">
<map>
<entry key="statType" value="2" />
<entry key="switchs" value="${statistic.area.device.online.rate.switchs}" />
<entry key="deviceOnlineRateAreaJobService" value-ref="deviceOnlineRateAreaJobService"></entry>
<entry key="machineOnlineRateAreaJobService" value-ref="machineOnlineRateAreaJobService"></entry>
<entry key="deviceOnlineRateAreaCityJobService" value-ref="deviceOnlineRateAreaCityJobService"></entry>

</map>
</property>
</bean>
。。。。。
定义了多个需要执行的类
<!-- 定时任务触发器配置  在线率按区域统计每天执行一次-->
<bean id="deviceOnlineRateAreaTaskQuartzDayTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="deviceOnlineRateAreaTaskQuartzDay" />
<property name="cronExpression" value="${statistic.area.device.online.rate.day}" />
</bean>
这块也继续用到读取配置什么时间执行那个上面定义的具体类
<!-- 启动定时任务 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="deviceOnlineRateAreaTaskQuartzDayTrigger" />
<ref bean="deviceOnlineRateAreaTaskQuartzWeekTrigger" />
<ref bean="deviceOnlineRateAreaTaskQuartzMonthTrigger" />

<ref bean="deviceOnlineRatePoliceTaskQuartzDayTrigger" />
<ref bean="deviceOnlineRatePoliceTaskQuartzWeekTrigger" />
<ref bean="deviceOnlineRatePoliceTaskQuartzMonthTrigger" />

<ref bean="deviceOnlineRateManufacturerTaskQuartzDayTrigger"/>
<ref bean="deviceOnlineRateManufacturerTaskQuartzWeekTrigger"/>
<ref bean="deviceOnlineRateManufacturerTaskQuartzMonthTrigger"/>
</list>
</property>
</bean>
最终底下通过调用执行这些定时
上面调用具体的job方法调用的具体的实现方法需要get,set类引入
public class DeviceOnlineRatePoliceTask extends QuartzJobBean {
private DeviceOnlineRatePoliceJobService deviceOnlineRatePoliceJobService;
@Override
protected void executeInternal(JobExecutionContext jobContext)
throws JobExecutionException {
try {
log.debug("Area Online Rate Task Begin,The statType is :" +statType);
doWork();
log.debug("Area Online Rate Task End,The statType is :" +statType);
} catch (Throwable e) {
log.error("Do Device OnlineRate police Task fail: ", e);
}
}

private void doWork() throws Exception{
if (switchs.equalsIgnoreCase("ON")) {
deviceOnlineRatePoliceJobService.doWork(statType);
}
}
public DeviceOnlineRatePoliceJobService getDeviceOnlineRatePoliceJobService() {
return deviceOnlineRatePoliceJobService;
}

public void setDeviceOnlineRatePoliceJobService(
DeviceOnlineRatePoliceJobService deviceOnlineRatePoliceJobService) {
this.deviceOnlineRatePoliceJobService = deviceOnlineRatePoliceJobService;
}
}
}

2.工厂模式:这个很明显,在各种BeanFactory以及ApplicationContext创建中都用到了。(各个类可以通过配置xml来实现动态加载)
spring中的IOC是Inversion of Control的缩写控制反转

Spring中提供了两种IoC容器:
BeanFactory
ApplicationContext

这两个容器间的关系如下图:

我们可以看到,ApplicationContext是BeanFactory的子类,所以,ApplicationContext可以看做更强大的BeanFactory,他们两个之间的区别如下:

BeanFactory。基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
ApplicationContext。ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等,ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场中,ApplicationContext类型的容器是比较合适的选择。

3.单例模式(Singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。

spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为spring管理的是是任意的java对象。

spring配置文件中的bean默认情况下是单例模式scope=”singleton”,
如类中注解
@Autowired
MyClass ma;//不管多少个请求、线程,访问的都是同一个ma对象

比如spring中加载数据库配置的连接。

4.适配器(Adapter)
在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。Spring实现这一AOP功能的原理就使用代理模式(1、JDK动态代理。2、CGLib字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现的面向切面编程。
AOP核心概念

一、切入点(pointcut)

需要要增强的目标方法,比如:你的一个类有很多方法,但是只需要在login方法执行记录一下日志,其他的方法不需要,那么这login方法就是一个切点

二、连接点(joinpoint)

这里指的是方法连接点,连接点可以说是切点的全集。方法有关的前前后后都是连接点。

三、通知(advice)

某个方法在执行前或执行后需要完成的工作,比如在某个方法执行完成后记录一下日志。在什么时候和要完成什么通过方法名中的before,after,around等来实现

四、切面(aspect)

切面是通知和切点的结合,切点和通知共同定义了切面的全部功能——它是什么,在何时何处完成其功能 , 切面就是对横切关注点的抽象
例如项目中用到的操作日志,
利用AOP代理
动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

如果目标类没有实现接口,那就要选择使用CGLIB来动态代理目标类。
CGLIB会让生成的代理类继承当前对象,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。
在CGLIB底层底层实现是通过ASM字节码处理框架来转换字节码并生成新的代理类
注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
在servlet.context.xml中
<识别切面,开启CGLIB动态代理>
<aop:aspectj-autoproxy proxy-target-class=“true”/>
/**
*自定义注解 拦截Controller
*/

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemControllerLog {
String LogAction() default “”;
String LogContent() default “”;
int ModuleID() default 0;
}

/**

  • 切点类

  • @author

  • @since 2016-09-05

  • @version 1.0
    */
    @Aspect
    @Component
    public class SystemLogAspect {
    @Autowired
    public JdbcCommonDao jdbcCommonDao;
    @Autowired
    public SystemLogService logService;

    // 本地异常日志记录对象
    private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);

    // Controller层切点
    @Pointcut("@annotation(com.netbox.service.common.annotation.SystemControllerLog)")
    public void controllerAspect() {
    }

    /**

    前置通知 用于拦截Controller层记录用户的操作
  • @param joinPoint
  • 切点

*/
@Before(“controllerAspect()”)
public void doBefore(JoinPoint joinPoint) {

try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
SystemLogModel slm = getControllerMethodDescription(joinPoint);
logService.logService(slm.getModuleID(), slm.getLogAction(), slm.getLogContent(), request);
} catch (Exception e) {
// 记录本地异常日志
logger.error("==前置通知异常==");
logger.error("异常信息:{}", e.getMessage());
}

}

/**

  • 前置通知 用于拦截Controller层记录用户的操作
  • @param joinPoint
  • 切点

*/
@Deprecated
// @Before(“controllerAspect()”)
public void doBeforeback(JoinPoint joinPoint) {

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpSession session = request.getSession();

// 读取session中的用户
// User user = (User) session.getAttribute(WebConstants.CURRENT_USER);
// 请求的IP
String logIP = request.getHeader("X-Real-IP");
if (StringUtils.isEmpty(logIP)) {
logIP = request.getRemoteAddr();
}
try {
SystemLogModel slm = getControllerMethodDescription(joinPoint);
slm.setLogIP(logIP);
slm.setTimeFlag(DateUtils.currentDatetime());
if (null == request.getParameter("UserID") || null == request.getParameter("UserName")) {
logger.debug("操作日志-->日志添加:用户名或用户ID为空,返回不添加日志!");
return;
}

slm.setFlagID(Integer.parseInt(request.getParameter("UserID").toString()));
slm.setFlagName(request.getParameter("UserName").toString());

// *========控制台输出=========*//
logger.debug("=====注解参数获取开始=====");
logger.debug("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
logger.debug("操作模块:" + slm.getModuleID());
logger.debug("操作方法:" + slm.getLogAction());
logger.debug("操作内容:" + slm.getLogContent());
logger.debug("请求IP:" + slm.getLogIP());
logger.debug("FlagID:" + slm.getFlagID());
logger.debug("FlagName:" + slm.getFlagName());
// *========数据库日志=========*//
String sql = "insert into [WFBDCMain].[dbo].[ISIP_log_Operation]([FlagID],[FlagName],[LogAction],[LogContent],[LogIP] ,[ModuleID],[TimeFlag])"
+ " values("
+ slm.getFlagID()
+ ",'"
+ slm.getFlagName()
+ "','"
+ slm.getLogAction()
+ "','"
+ slm.getLogContent()
+ "','"
+ slm.getLogIP() + "'," + slm.getModuleID() + ",'" + slm.getTimeFlag() + "')";
jdbcCommonDao.delete(sql);

} catch (Exception e) {
// 记录本地异常日志
logger.error("==前置通知异常==");
logger.error("异常信息:{}", e.getMessage());
}

}

/**

  • 获取注解中对方法的描述信息 用于Controller层注解

** @param joinPoint**

  • 切点
  • @return 方法描述
  • @throws Exception
    */
    public static SystemLogModel getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
    String targetName = joinPoint.getTarget().getClass().getName();
    String methodName = joinPoint.getSignature().getName();
    Object[] arguments = joinPoint.getArgs();
    Class targetClass = Class.forName(targetName);
    Method[] methods = targetClass.getMethods();
    String description = “”;
    SystemControllerLog log;
    SystemLogModel logM = new SystemLogModel();
    for (Method method : methods) {
    if (method.getName().equals(methodName)) {
    Class[] clazzs = method.getParameterTypes();
    if (clazzs.length == arguments.length) {
    log = method.getAnnotation(SystemControllerLog.class);
    logM.setModuleID(log.ModuleID());
    logM.setLogAction(log.LogAction());
    logM.setLogContent(log.LogContent());
    break;
    }
    }
    }
    return logM;
    }
    }

5.包装器
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。

Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。

首先想到在spring的applicationContext中配置所有的dataSource。这些dataSource可能是各种不同类型的,比如不同的数据库:Oracle、SQL Server、MySQL等,也可能是不同的数据源:比如apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。然后sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。

6.代理(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。 从结构上来看和Decorator模式类似,但Proxy是控制,更像是一种对功能的限制,而Decorator是增加职责。
spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。
静态代理
主要完成的就是在不影响旧接口业务的情况下加入新业务继承于原方法接口并重新实现该接口称为代理类

动态代理
我们需要再编写一个用于处理扩展业务的类:
package proxy.part02;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**

  • 扩展业务的处理对象,要实现Java反射包中的InvocationHandler接口
    */
    class ServiceHandler implements InvocationHandler{
    //定义目标对象的引用,并建立联系
    private Object target;
    public ServiceHandler(Object target) {
    this.target=target;
    }
    /**执行代理对象的业务方法时,会自动执行这个handler对象的invoke方法 @param proxy 指向代理对象
  • @param method 指向接口中的方法对象
  • @param args 指向method对象执行时需要的实际参数
  • */
    @Override
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
    System.out.println(“begin transaction”);
    //执行目标对象的method(核心业务)方法,参数为args
    Object result=method.invoke(target,args);
    System.out.println(“end transaction”);
    return result;
    }
    }

在使用时动态加载
import java.lang.reflect.Proxy;
/**

  • 测试JDK动态代理
    /
    public class TestDynamicProxy {
    public static void main(String[] args) {
    //创建一个目标对象
    OrgSerivce orgService=new OrgServiceImpl();
    //获得这个目标对象的类加载器对象
    ClassLoader loader=orgService.getClass().getClassLoader();
    //获得目标对象所实现的接口的Class对象
    Class<?>[] interfaces=orgService.getClass().getInterfaces();
    //创建一个Handler对象,处理扩展业务
    ServiceHandler handler=new ServiceHandler(orgService);
    /*
    * 通过Proxy.newProxyInstance方法获得代理对象
    * @param loader 目标对象的类加载器对象
    * @param interfaces 目标对象实现的所有接口的Class对象数组
    * @param handler 处理扩展业务的对象
    */
    orgService=(OrgSerivce)Proxy.newProxyInstance(loader,interfaces,handler);
    //执行代理对象业务方法
    orgService.saveOrg(“软件开发部”);
    orgService.updateOrg(1);
    }
    }

7 观察者(Observer)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
spring中Observer模式常用的地方是listener的实现。如ApplicationListener。

8:策略(Strategy)

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
spring中在实例化对象的时候用到Strategy模式
在SimpleInstantiationStrategy中有如下代码说明了策略模式的使用情况:

9.模板方法(Template Method)
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Template Method模式一般是需要继承的。这里想要探讨另一种对Template Method的理解。spring中的JdbcTemplate,在用这个类时并不想去继承这个类,因为这个类的方法太多,但是我们还是想用到JdbcTemplate已有的稳定的、公用的数据库连接,那么我们怎么办呢?我们可以把变化的东西抽出来作为一个参数传入JdbcTemplate的方法中。但是变化的东西是一段代码,而且这段代码会用到JdbcTemplate中的变量。怎么办?那我们就用回调对象吧。在这个回调对象中定义一个操纵JdbcTemplate中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到JdbcTemplate,从而完成了调用。这可能是Template Method不需要继承的另一种实现方式吧。

以下是一个具体的例子:
JdbcTemplate中的execute方法

JdbcTemplate执行execute方法

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: