框架学习记录

设计模式

1.概念

什么是设计模式:
设计模式可以被看成是前人所总结的经验,套路
特点:
是一套被反复使用的,多数人知晓的,经过分类编目/代码设计经验的总结。(分类当时的GOF已经完成了的)
优点:
代码质量好 可靠性高。
复用代码。代码重用。
代码更规范,更容易被人理解。

软件设计的原则(评判标准):
设计的主要方式:提取抽象,隔离变化。

SOLID设计原则 唯一不变的就是变化
-S 单一责任原则:
把一个模块(API/类/功能的modeule) 具体负责一个功能,一类功能

-O 开放封闭原则 :
对(增加代码)开放:可以新增功能的API
对(修改代码)封闭:默认之前的代码都是合理的

-L Liskov原则:
凡是可以出现接口的地方,都可以出现接口的实现。
父类可以使用的地方,子类也可以使用。

-I 接口隔离原则:

常见的设计模式:

2.设计模式分类

创建型模式()

单例模式(Single ):

建造者模式(Builder Pattern):

也是一种产生类的模式,将一个复杂的对象的构建与他的实例分离,使得同样的构建过程可以创建不同的实例。
包括的角色
Builder(接口)
ConcreteBuilder(实现)
Director(导演,负责产品的生产流程,其实就是使用者,定制实例的人)
Product(产品)
Demo:

分析:
好处:可以定制。
坏处:开销较大

工厂模式(Factory):

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且通过使用同一个接口来指向新创建的对象。
什么情况下使用工厂?
通常是某些类 构建() 初始化过程比较复杂,而只需要传递一个简单的参数,就可以获得一个复杂的对象。
比如如果工厂类还可以set的话,这就需要学习成本了。
简单工厂:给我需要使用的类的名字,简单工厂返回一个特定类的实例。
简单工厂中如果有很多个特定类(一个父类,一群子类),那么就可以通过switch语句来创建不同子类的实例
Demo:
弊端:有悖开闭原则(对增加代码开放,对修改封闭)

工厂方法
解决上述简单工厂要修改代码的风险,新增去实现工厂方法的接口,接口中提出工厂的通用方法,这样就将方法通过接口隔离出来,然后创建各个特定类。
好处:不需要影响已有代码。

抽象工厂模式:

原型模式:


结构性模式

装饰者模式:

回顾:过滤器(request)/线程池(close)放回连接池
包装的做法:

1.被包装类要有一个共同的接口(Connection接口)
被包装类(mysql实现的Connection)
包装类(MyConnection 包装之后的类)
里面包装了一个被包装类的实例,通过构造方法传入。不需要修改的方法,直接调用被包装类的实例去完成(实现相同的接口),需要修改(增强)的方法,包装类自己实现。

好处:

在不改变原来的API的前提下,改变API的功能——增强。

适配器模式:

实现和被增强的类相同的接口,但是没有重写任何方法。如果其他类需要增强,只需要继承适配器,仅仅重写需要增强的方法就行了。
本身是一种提供方便的中间类。

代理模式(重要 Proxy):

分类:动态代理/静态代理

概念:代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类 负责为 * 委托类(访问的对象)* 预处理消息,过滤消息转发消息,以及进行消息被委托类执行后的后续处理。

概念:

为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息(最终还是需要使用带被代理对象),以及进行消息被委托类执行后的后续处理。

好处:
静态代理:

就是包装的设计模式(包装类 可以看出是委托类,被包装类看成代理类。)
* Demo:*

动态代理:

**定义:**Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。
生成技术:
1.jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象。
2.cglib (spring里再学习)
JDK中的类介绍:
java.lang.reflect.Proxy: 这是生成代理类的主类
* Proxy 类主要方法*:
Proxy.newProxyInstance():产生代理类的实例。仅能代理实现至少一个接口的类。(构造方法要传三个参数进来)
ClassLoader:类加载器。固定写法,和被代理类使用相同的类加载器即可。(被代理类.getClass().getClassLoader()是一个通用的写法)
Class[] interface:代理类要实现的接口。固定写法,和被代理类使用相同的接口即可。
InvocationHandler:策略(方案)设计模式的应用。

Proxy.newProxyInstance(owner.getClass().getClassLoader() , owner.getClass().getInterfaces() , 
    //callback函数:rentHouse方法调用的时候调用 
    new InvocationHandler(){
    @Override
    public Object invoke(Object proxy,Method method,Object[] args){
    //三个参数:
    //Object proxy:proxy.newProxyInstance 产生的代理对象的引用
    //Method method:接口方法的实例(是你当前调用的代理对象的方法)
    //Object[] args:代理对象传入的参数值(是你当前调用代理对象的方法的饿时候传入的参数值,可能是多个)

    }
    })

动态代理的内部实现:
调用代理类对象的方法,这个方法会调用InvocationHandler的invoke(…)方法,然后可以在invoke方法中进行预处理,然后再调用被代理对象的方法。返回一个invoke对象的实例。
动态代理的优点:
1、职责清晰:
2、可以在不改变原有类的实现的前提之下,去给这个类的某些方法进行增强。(切面编程)
3、高扩展性
优化事务处理:

外观模式

桥接模式

组合模式

享元模式

行为型模式

策略模式:

模板方法模式:

观察者模式:

迭代子模式:

责任链模式:

命令模式:

备忘录模式:

状态模式
访问者模式
中介者模式
解释器模式:


Spring

Spring介绍(官网:Spring.io)

是由Rod Johnson创建的一个开源框架。有很多适用范围。
最初只是:Spring Framework(起源),因为sun公司提供的EJB框架太复杂了,在Spring和Hibernate等框架出现后,大量用户流失。
Spring是一个充分利用解耦合的思想的框架,在Spring框架中时钟贯穿面向接口编程的思想。

Spring的本质:

目前Spring是一个及其庞大的开源体系,这里暂时只将Spring Framework
Spring 的核心就是控制反转(IOC:Inverse Of Control)/依赖注入(DI:Dependency Injection)*/AOP(面向切面的编程)*

Spring的优点:

- 方便解耦,简化开发(最核心)

Spring 就是一个大工厂

- AOP编程的支持(最核心)

- 声明式事务支持(依赖上面两个优点)


Spring IOC 控制反转

Inverse of control

简介:

控制什么? 我们去控制service的对象的生成 (实例)—> 控制对象的生成
反转什么?控制权 由我们自己 反转 到的Spring的框架。
——之前由程序员手动的去创建对象实例的过程,引入spring框架之后,由spring帮我们去完成。

导包:

5+1(Spring5.0之后):5个jar包(core/expression/context/bean/aop) + 1个依赖包(org..apache.commons.logging)

导入约束:

在Spring 的文档中找约束:
spring-framework-3.2.0.RELEASE/docs/spring-framework-reference/html/xsd-config.html

通过配置XML文件的内容生成对象:

XML文件:

在测试中的API :
//由Spring来创建
new ClassPathXmlApplicationContext(“applicationContext.xml” );

Spring DI 依赖注入(Dependency Injection)

Bean A里 依赖的成员 Bean B

上面的IOC和DI都是通过配置文件来完成,在类中只需要几句话,就可以达到解耦的目的

ClassPathXmlApplicationContext service = new ClassPathXmlApplicationContext(“applicationContext.xml” );
但是还需要考虑一个地方,servlet中的service并不是由spring完成注入的,后面在spring中会学习到。

Spring的其他细节

Spring framework的架构

见图

ApplicationContext

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml” );
可以从application.getbean(“”)得到bean Container。

ClassPathXmlApplicationContext /FileSystemXmlApplicationContext

ClassPath会在当前应用下,去找这个xml文件,FileSystem则要得到文件路径,也能够加载bean。
BeanFactory结构

装配Bean

什么是装配Bean:项目中一个类依赖另一个类,Spring 在管理bean的时候,通过配置文件

Bean的实例化方式:

默认构造(无参构造)

大多数Spring 管理Bean都是通过反射机制,反射机制都需要无参构造方法来实现。

静态工厂

可参考 > https://www.jianshu.com/p/ceb5ec8f1174

介绍:

常用于Spring整合其他框架(或者工具类)
为了使用spring重构之前的代码,用Spring来进行解耦,我们之前的类里可能是用静态工厂来提供javabean的,现在加入spring之后就可以用spring的静态工厂去构造。
用于生产实例对象,的方法都必须是静态方法

代码:

<bean name="..." class="...UserFactory" factory-method="工厂类中的方法">——静态工厂(工厂类中的获取 bean 方法必须是静态方法

实例工厂

先实例化工厂,再通过工厂对象来调用方法
<bean name="AAA" class="...UserFactory">
<bean name="BBB" factory-bean="AAA" factory-method="工厂类中方法">

Bean的作用域(<bean scope="singleton或prototype">)

Singleton:在没有配置作用域的时候,默认是单例模式,也就是通过name来获取bean实例的时候,name相同会获取同一个对象。
protoytpe:配置后,为每次调用都创建一个新的对象。

Bean的生命周期

什么时候创建:

**获取Bean的实例的时候才创建
(scope为prototype时候,要在getBean的时候创建;如果是Singleton的话,在加载xml文件(new ClassPathXmlApplicationContext (“….xml”))的时候就会创建)**

什么时候销毁:

容器销毁的时候才销毁。

详细的生命周期 之:BeanPostProcessor

<bean name="..." init-method=">调用bean中的方法初始化” destroy-method=”销毁时调用的方法”>(init-method和destroy-method方法都是回调方法)
初始化是在给到bean 之前就会调用。
在init-method方法回调之后,还会调用BeanPostProcessor方法:
里面会有两个回调函数:(其中两个参数Object bean:当前创建的bean;String beanName:当前bean的名字),这两个回调函数的返回值就是这个bean。
配置xml:<bean class="..." >这里可以不需要name属性,因为Spring的语法,在加载的时候会去找有没有这个class


基于Xml的装配Bean

基于构造方法:

<bean>
<constructor-arg name="username" value="zs">
<constructor-arg name="password" value="123">
<constructor-arg name="address" ref="addr">
</bean>
<bean name="addr" class="...."></bean>

此时获取的bean就会调用这个有参构造,将这些值传入进去。

基于setter方法:

前提是这些方法都要有set方法,是从set方法来找,而不是成员变量名

<bean>
<property name="username" value="zs">
<property name="password" value="123">
<property name="address" ref="addr">
</bean>
<bean name="addr" class="...."></bean>

其他的case:

比如如果password是一个数组(或者List<>、Map<>、Properties):

<!-- 数组 -->
<property name="password">
    <array>123</array>
    <array>345</array>
</property>

在注入的时候,如果,那么只要对应的类中有getUsername(){}和setUsername(){}方法,就可以找到,不管类中的成员变量的名称是什么。。。


基于注解的装配Bean

增加注解的扫描:

@Component //注解的意义:创建一个类的实例,但是还没有名字,就无法获取到,所以还需要@Component(“myuser”),但是发现还是无法获取到,这个是有就要在XML中告诉jvm要扫描注解
扫描注解的XML:先找到namespace:

xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context/spring-context.xsd"

<context:component-scan base-package="com.cskaoyan"/>(这个包路径根在xml的同级目录)
使用注解的xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        ">
<!--扫描spring的注解,如果需要的话,这里的base-package直接写包名,然后会在包下扫描-->
<context:component-scan base-package="HomeWork4"/> 

@Component(“xxx”)取代了
如果想要prototype,但是Component中没有scope,引入一个新的注解:@Scope(“prototype”):默认也是单例

XML注解能做的

1.自定义的init()方法:@PostConstruct

2.自定义的destroy()方法:@PreDestroy

3.通过注解进行属性的依赖注入

在成员变量之上@Value(“要注入的value值”)

4.引用类型:@Resource(name=”xxx”)取代<bean name="xxx" ref="aaaa">

5.按类型注入@Autowired:这个类型应该是唯一的,不能有两个实例(可以加一个@Qualifier(”bean名称”)来指定是哪个bean)

6.注解的变种:@Repository : 注解Component的别名 / @Service : 注解Component的别名。为了提高可读性,才设置这样的别名,在不同的层次使用不同名的注解:

业务层用@Service,mvc层的bean 用@Controller ,Dao层用@Repository

注解不能做,只能XML做的

1.集合类型的属性值的依赖注入的时候,如果是数组,就不能使用@Value(“…”)只能通过XML的<Array><value>...</value></Array>
2.已经编译好的类/第三方库,也无法使用注解

依赖注入:通过xml的property进行装配bean的时候,是调用setter来注入的。那么通过注解呢?

如果写在字段上,是通过反射这个Field;
如果写在Setter方法上,是通过Setter来注入的

注解装配Bean 和 xml装配Bean

注解的优点:简单直观

Xml:全面,部分情况注解无法处理,只能xml里配置。

注解的一些详细使用参考:https://www.cnblogs.com/xiaoxi/p/5935009.html

常见注解归纳:
Spring常用注解汇总
本文汇总了Spring的常用注解,以方便大家查询和使用,具体如下:

使用注解之前要开启自动扫描功能,其中base-package为需要扫描的包(含子包)。
<context:component-scan base-package="cn.test"/>

@Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
@Scope注解 作用域
@Lazy(true) 表示延迟初始化
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Scope用于指定scope作用域的(用在类上)
@PostConstruct用于指定初始化方法(用在方法上)
@PreDestory用于指定销毁方法(用在方法上)
@DependsOn:定义Bean初始化及销毁时的顺序
@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:
@Autowired** @Qualifier(“personDaoBean”)** 存在多个实例配合使用
@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
@PostConstruct 初始化注解
@PreDestroy 摧毁注解 默认 单例 启动就加载
@Async异步方法调用


AOP

- 引入

面向切面编程:

Aspect Oriented Programming

AOP -VS- OOP

面向对象编程 oop:Object Oriented Programming
类的增强是通过继承父类,然后增强子类。
面向切面编程
比如有一个类,其中有一个方法,将其“切开”,然后再”增强”(动态代理),然后再复用之前的代码。

介绍:

在软件业,面向切面编程,是指通过预编译方式 和 运行期 动态代理实现程序功能的同一维护的一种技术。AOP是OOP的延续。

AOP的特点:

AOP采取横向抽取机制,有别于 传统的纵向继承体系重复性代码。

AOP应用:

事务管理、性能监视、安全检查、缓存、日志等。

AOP的原理:

动态代理的两种方式:

JDK动态代理

代理对象跟被代理对象要有相同的接口,才能实现。
如果目标类没有实现任何接口呢?jdk动态代理就无能为力了。

Cglib动态代理

基于继承,也可以实现动态代理。可以写一个子类,里面有父类的所有方法,但要求父类(被代理对象)不能是final的。

AOP术语:

Target 目标类:需要被代理的类,委托类
Proxy 代理类:动态代理生成的
JoinPoint 连接点:指 被代理对象里 那些可能会被增强的点(方法)的所有 方法 。(候选的可能被增强点)
PointCut 切入点:已经被增强的连接点(方法)。
Advice 通知(增强):代理对象 执行到Joinpoint所做的事情(具体的增强代码)。
织入(植入) weaver:指把advice应用到 目标对象 来创建 新的代理对象的过程。
Aspect 切面:是切入点和通知的结合(一个切入点和一个通知组成一个特殊的面)


AOP实战

1.手动方式:

配置环境(4+1+2)

1、JDK动态代理

2.cglib动态代理:用子类产生代理。

不能使用final修饰。

2.SpringAOP实现:

思路:和手动的一样,都会产生一个代理对象。
做法:把如何产生代理对象交给Springaop,同时,把增强代理对象交给用户。
工厂的话,Spring可以提供:
配置如下:

<!--需要一个被代理对象的实例-->
<bean class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 通知 -->
<bean name="myadvice" class="..." />
<!-- 被代理对象 -->
<property name="target" ref="myservice"/>
<!-- -->
<property name="interceptorName" value="myadvice">

那么通知必须要实现一个接口。

QA

1.

2.Spring的AOP是通过哪种代理方式产生动态代理?

有接口就默认用jdk,没有接口就用cglib

3.那么如果有接口的话,能不能用cglib?

可以<property name="optimize" value="true">

不足之处:

还不够灵活。
能不能随意指定一个类,里面的方法都能作为通知?。
哪些方法被增强需要些代码判断,能不能通过配置判断。

引入AspectJ

什么是AspectJ

是一个面向切面的框架,拓展了java语言,定义了AOP语法,所以有一个专门的编译器用来生成遵守Java字节码编码规范的Class文件。。。
###主要解决的问题:切入点表达式、通知的类型

代码过程:

导包: 5 + 1 + 2(spring-aspectj-weaver….jar)
aspectj的增强的配置:

<aop:config>
    <!--pointcut表示切入点,具体需要增强的方法-->
    <aop:pointcut id="mypointcut" expression=" 权限修饰+返回值+全类名(参数类型列表)">
    <aop:advisor advice-ref="通知的名字" pointcut-ref="mypointcut">
</aop:config>

这段配置实现的原理:通过“通知”来告诉增强的方法, 然后再aspectj中使用配置,来指定切入点和增强的方法(advice-ref:通知)

AspectJ深入学习

切入点表达式:

case1:可以方便地指定具体的某一个方法。
case2:可以使用通配符去指定某一些方法。
<aop:pointcut id="mypointcut" expression=" 权限修饰+返回值+全类名(参数类型列表)">
访问修饰符:可以省略,省略表示所有。
方法返回值:不能省略,可以用 * 代替。
。。。。

通知的方式:

POJO(Plain and Old Java Object):随便写个类,不需要实现某个接口,但是可以作为增强代码。


public class MyPOJOAdvice{
    //能不能这个方法在某些方法之前执行
    public void mymethod1(JoinPoint point){
        sout("begin");
    }
}

如果要实现这个功能,就要修改之前的配置:

<bean name="myadvice2" class="....">
<aop:config>
    <!--pointcut表示切入点,具体需要增强的方法-->
    <aop:aspect ref="myadvice2">
    <aop:pointcut id="mypointcut" expression=" 权限修饰+返回值+全类名(参数类型列表)">
    <aop:before method="类中的方法名" point-cut="mypointcut" />
    </aspect>
</aop:config>

还可以通过JoinPoint来配合aop:before进行前置的校验。。在类中point.get……各种东西(签名、传入的参数等等…..)

如果对同一个方法增强2个Advice,2个切面表达式相同,可能会报错,原因可能为接口不能向下转型。


JDBCTemplate

1.引入

Spring提供的数据库框架(轻量级的,类似DBUtils)
是对操作数据库的语句进行模板化的封装,让开发者只关注于数据库的sql语句即可。
JDBCTemplate也要依赖于数据源。

2.JDBCTemplate的使用

强调:Spring整合其他的框架的基本思路。
Spring的两个核心功能:IOC+DI 和 AOP
凡是之前new出来得到的对象都让Spring去生成,然后通过装配bean来组装。

2.1导包:

5+1基本包 , 操作数据库的:数据库驱动(mysql-connection.jar) + 数据源(C3P0或DBUtils)
Spring里面的jar包 : jdbc-spring / jdbc-tx.jar

2.2使用:

在DaoImpl里面使用Spring提供的JDBCTemplate来操作数据库。

JdbcTemplate jdbcTemplate ;//(通过注入来生成这个对象);
//方法中(query的重载方法很多)
jdbcTemplate.query("sql语句",new BeanPropertyRowMapper<User>(User.class),username,password){
return query;
}

在xml中配置(配置项:service—dao—jdbcTemplate—dateSource—注入dateSrouce的参数。):

<bean name="" class="">
<property name="" class="">
</bean>
<bean></>
<bean></>
<bean></>

见图

用注解进行配置:
见图

可以增加注解的类,我们自己写的,但是对于第三方的类,我们还是需要在xml里面装配。(比如jdbcTemplate)

还可以在Dao里面继承 JdbcDaoSupport ,就不需要在Dao里面获得Template对象了,可以直接调用父类的getTemplate方法,就要通过XML配置注入。
还想再优化一点:
查看JdbcDaoSupport的反编译代码,发现里面有setDateSource方法,里面会new一个Template 所以在xml里面可以省略jdbcTemplate,如下图

如果确实想要注解注入的话?
在Dao里面自己生产一个方法,

@Autowired
getDS(DateSource ds){
setDateSource(ds);
}

那么在XML文件里,只需要扫描注解 + dateSource(的配置)
不能通过@PostConstruct 一个有参构造传入,
但可以通过@Autowired 一个有参构造传入,

@Autowired
构造方法(DateSource ds){
setDateSource(ds);
}

2.3JDBCTemplate的CRUD

**增:**getTemplate().update(“SQL语句”,Object…arg)

update 增删改

query 查


Spring整合Junit(对测试框架的支持)

1.导包:

test.jar / tx.jar

2.希望在测试框架中不需要添加其他的准备工作(比如beanContener 的装配(classPathXml…(..xml)))

使用@RunWith(SpringJUnit4ClassRunner.class)

配合使用@ContextConfiguration(locations=”classpath:applicationContext.xml”)

然后就只需要:

@Autowired
private UserService userSerivce;
就可以省略如下的在每次测试中都需要输入的代码:
ClassPathXmlApplicationContext cpxac = new ClassPathXmlApplicationContext(“springaop2.xml”);
UserService userService = (UserService)cpxac.getBean(“userServiceName”);


Spring事务管理

1.事务管理常用对象

1. PlantformTransactionManager
2. TransactionDefinition
3. TransactionStatus

1.1 PlantformTransactionManager (接口)

平台事务管理器,专门管理事务的。
Spring可以管理任何实现了这些接口的事务。

1.2 TransactionDefinition

这个接口的作用就是定义 事务 的名称、隔离级别传播行为、超时时间长短、只读属性等。

传播行为:在两个业务之间共享事务

  1. TransactionDefinition.PROPAGATION_REQUIRED : 支持现有的事务,如果没有则新建一个事务。
  2. .PROPAGATION_SUPPORTS : 支持现有的事务,如果没有则以非事务状态运行
  3. .PROPAGATION_MANDATORY : 支持现有事务,如果没有则抛出异常
  4. . PROPAGATION_REQUIRES_NEW : 总是发起一个新事务,如果当前已存在一个事务,则将其挂起。
  5. .PROPAGATION_NOT_SUPPOERTED:不支持事务,
    …在不同程度上支持事务(必须有事务——> 必须没有事务)

1.3TransactionStatus

这个接口的作用就是获取事务的状态(回滚点、是否完成、是否新事务、设置回滚、是否回滚、刷新)

1.4代码Demo

导包:没有新包

Demo1:使用原始的TransactionTemplate实现:

transactionTemplate.excute(

    new TransactionCallback<Object>(){
      @Override
      public Object doInTransaction(){
        //里面放入需要事务的操作
        ....
        return (操作的返回值,可让外界获得)
      }
    }   
);
//如果不想要返回值的话,就使用TrasactionCallbackWithoutResult()
在excute()中已经开启了事务,这就是spring框架对事务的支持。

然后再配置下,给到事务和Dao层 同一个连接,才能开启事务Ctrl+H(查看父类)

<bean class="org.springframework.transaction.support.TransactionTemplate" ><property name="" ref="aaa">

<bean name="aaa" class="org.springframework.jdbc.datasource.DateSourceTransactionManager">

Demo2:使用事务模板配置实现

Spring 的原AOPProxyFactoryBean
原理:配置一个Service的代理类,去增强service的转账方法

<bean name="myproxy" class="org.springframework.transaction.interceptor.TransactionFactoryBean">
<property name="target" ref="myservice">//目标对象
<property name="transactionManager" ref="">//增强用哪个管理器
<property name="transactionAttributes">
    <props>
        <prop key="*(哪个方法使用事务)">配置事务的隔离级别、只读(readOnly)、回滚等</prop>
    </props>
</property>

然后在增强的对象上

@Resource

Demo3:SpringAspectJ 增强控制事务

<aop:config>
    <!--增强谁-->
    <aop:advisor advice-ref="mytxadvice" pointcut="execution()切入点表达式" />
</aop:config>

<!--自己配置的-->
<tx:advice id="mytxadvice" transaction-manager="">
    <tx:attributes>
        <tx:method name="方法名" propagation="传播行为" isolation="隔离级别">
    </tx>
</tx:advice>

FinalDemo:注解实现

<bean name="Manager名" class=".....transactionDataSourceTransactionManager">
    <property name="datesource" ref="mydatasource">
</bean>
<tx:annotation-friven transaction-manager="Manager名称">

然后在要增加事务的方法上@Transactional(里面可以增加各种事务的处理方法)
propagation=Propagation.REQUIRED。


SpringMVC

SSM中的一个SSSH中是Structs来代替SpringMVC
优化之前的商城的代码:,接收到op,然后判断调用方法。
1.使用反射重构代码。然后可以创建一个baseServlet来接收所有的op;
2.封装请求参数也要自己写代码处理;
3.结果视图耦合性强:还有就是在servlet中的方法里,对接收的数据进行处理和转发,如果要更改需求,就要修改源码,这个就很不方便了。之前的代码转发到不同的结果视图是已经写死了。

引入Springmvc

优点:

1.url和controller里的方法映射,很方便;
2.请求参数封装很方便;
3.结果/视图配置方便
4.Controller的实现有约束,不够灵活等

MVC和Springmvc:

MVC是一种架构,设计思想,Springmvc是这种架构下的mvc层的一个框架(工具类)
(SpringMVC是依赖于Spring的一个框架,必须要在Spring的环境下使用, 不像其他框架,可以相互独立)

springmvc在三层架构中的位置:见图

SpringMVC架构:

Springmvc是依赖于springframework下的一个架构。
见图,蓝色的部分需要手动实现。

1.入门案例 Demo1

1.1导包 5+1+2(web+webmvc)

1.2

自己定义一个类,处理页面请求;然后新建一个 web.xml(在WEB-INF下)找tomcat下的web.xml(项目部署的时候就会加载),将前面的约束复制过来

<!--要Spring去找 DisPathcerServlet-->
<servlet>
    <servlet-name>aaa</>
    <servlet-class>要关联的类</>
</servlet>
<servlet-mapping>
    <servlet-name>aaa</>
    <url-pattern>/myservlet</>
</servlet-mapping>

<!--配置DispatcherServlet-->
<servlet>
    <servlet-name>mydispathcer</>
    <servvlet-class>org.spring.....DispatcherServlet</>
    <init-param>
        <param-name>contextConfigLocation</>
        <param-value>classpath:applicationContext.xml</>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>mydispathcer</>
    <url-pattern>*.action</>
    <!--这个路径很重要,如果你在浏览器中输入可以匹配这个url的路径,就会由Spring来处理这个请求,而不会通过Tomcat的默认servlet来处理这个请求(404)-->
</servlet-mapping>

配置SpringMvc:
applicationContext.xml(在servlet实例化的时候才会加载)

<约束...>
<!--不需要写id或者name,反正这个bean已经有一个实例了,是配置HandlerMapping(处理器映射器,根据url找对应的方法(Handler))-->
<bean class="....BeanNameUrlHandlerMapping">

<!--处理器适配器(Adapter),执行Handler,得到Model and View-->
<bean class="....SimpleControllerHandlerAdapter">

<!--在浏览器中输入name的属性值,可以找到class的那个继承了Controller接口的类的实现方法(handlerRequest())-->
<bean name="对应的BeanNameUrl(通过名字来找)/hello.action" class="要Tomcat实例化的类com..controller.MyController">

如果拿到init方法中的参数,(参数在配置文件中已经生成)

实现请求处理类(重写Controller):

//指定一个url,去执行这个控制器里的某个方法
Mycontroller implements Controller{
    @Override
    public ModelAndView handlerRequest(HttpRequest ,HttpResponse){
    sout()
    ModelAndView modelandview = new ModelAndView(); 
    //如果没有模型也没有视图的话,那么返回的就是空,在浏览器中表现为404
    //设置模型(Model)

    //设置视图(View)
    modelandview.setViewName("写入jsp的名字(考虑是否需要加'/')")


    return modelandview;
    }

}

2.入门案例Demo2(注解配置)

2.1导包

2.2核心控制器(web.xml中配置的’servlet’)

取消配置的映射处理器和适配器。

servlet虽然是共享的一个(),但是不会出现线程安全问题,这是框架内部可以保证的。还要注意,在实现了Controller接口的类中不要写共享的变量。否则可能出现数据安全问题。

取消依赖的配置,使用注解:

<context:component-scan base-package="...">
<mvc:annotation-driven>
然后在实现Controller接口的类中
对类 : @Controller;
对需要操作的方法:@RequestMapping(“/hello.action”)这个名字是访问这个url的时候,就会调用这个方法。

@RequestMapping详解

1.可以省略默认后缀,

然后会自动匹配(value=”/hello”)或(path=”/hello”),然后在浏览器中输入hello.action依旧可以找到。
因为value是一个数组,所以可以这样写(path={“/hello”,”/cook”,”…”})

2.还可以配置多级目录:path={“/hello”,”user/cook”,”/wangdao”} 可以不写第一个 / 。这个 / 会给你自动加上。
3.路径可以通配:一个 * 代表一级路径。** 可以匹配多级目录。
4.字符可以匹配:单个?匹配单个字符,多个用 *
5.通配某个类 (窄化行为映射)在类上@RequestMapping(“/user”)要通配的URL,相当于给每个方法中的每个path都加了一个/user的前置路径。
6.属性 method=REQUEST.POST 只能通过post方式提交过来。如果不设置的话,就所有的提交方式都允许。
7.属性 params={id,age} 指定在提交时必须出现的参数字段。”….?id=xxx&age=zzz”
8.属性 headers ={“host=localhost”} 设置请求头的信息(根据字符串匹配)如果你写自己的ip地址,不行,因为根据字符串匹配。
9.关于path的总结:

1.如果两个方法的path相同,且没有其他参数就会报错
2.相同,但是只要@RequestMapping()中的其他参数不同,那么就不会报错,且能够找到相应的方法(比如method一个为GET,一个为POST)。
3.path可以不用加 ‘/’

如果只是访问方法的话,只需要两个注解就行了:@Controller / @RequestMapping
但是有注意点:对方法的访问权限无关/ 但跟 方法返回值类型有关。


Controller的实现方式

也就是对方法的实现。

方法一:实现Controller接口

方法二:一个纯的POJO(没有继承和实现的类)

对访问修饰符没有要求,对返回值有要求。

返回值:

1.可以是

ModelAndView  xxx(){
    new ModelAndView ;  
    return modelandview;
    }

2.可以是

 void xxx(HttpServletRequest request,HttpServletResponse response){
    request.setRedirct..().forword(req,resp);
    //或者
    response.setHeader(...)
    }

3.上面两种方式也不太建议这样写,
(推荐)物理视图路径:可以直接返回字符串(一个转发的地址,相对根目录下的路径 —— 物理视图路径)

@RequestMapping("路径a")
private String xxx(){
    //直接通过字符串的形式返回
    return "/index.jsp"
}

加了注解@RequestMapping下的return的底层就是转发(forward)
如果return不写 / 那么相对于注解中的路径的最后一个 / + return的内容

4.逻辑视图

返回的字符串,是物理视图字符串的一部分,可以得到一个完整的物理视图路径。那么就一定需要一定的规则来查找。
在Spring的配置文件中,配置视图解析器(ViewResolcer) 写入
写入物理路径的前面和后面(return中的路径的前后)

<bean class="org.......InternalResourceViewResolcer">
    <property name="pre" >
    <property name="" >
</bean>

但是由于视图解析器是一个全局的,所以如果不同文件夹下有同名jsp,就会出错。

模型Model

如何和前端的EL表达式联系起来,之前是放到request里面,从el表达式中取出来。
现在是通过ModelAndView来传递参数,
modelandview.addObject(name,value)
所以一般返回值就两种:String 和 ModelAndView
如果要携带参数的话:
1.可以是 String xxx(HttpServletRequest req){request.setAttribute(...)}

2.可以是 String xxx(Model model){model.addAttribute(....)}

3.可以是 ModelAndView xxx () { new ModelAndView ; modelandview.setViewName() }
考虑一下第一种方法:可以注入的参数有HttpServletRequset request、 response、session(session可以从request获取,也可以直接注入,反正都是同一个)
不过为了考虑解耦,最好就不要使用servlet的api(request/response/session)。

补充

1.return的默认值是forward(转发) 即:return forward:/index.jsp

**可以修改为重定向:**return redirect:给浏览器看的路径(如果有 / :相对于当web根目录下的资源 (/web) ;如果没有 / :则相对于当前访问的路径@RequestMapping(/a/b) ,即从/a开始拼接 路径)

2.@Controller

2.1 如果有多个配置的时候,那么就要在web.xml中一次性声明,形式如下:

<init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>classpath:SpringMVC1.xml,classpath:SpringMVC2.xml</param-value>
</init-param>

使用逗号隔开且每个都有一个classpath:
2.2 每个@Controller都需要声明一个value参数,否则在调用方法的时候就会报错

3.传入参数(自动参数映射)

3.1 基本数据类型

方法的参数可以是任意基本数据类型,如果方法参数名http中请求的参数名称 相同 时会进行自动映射。

3.2 自定义数据类型

除了基本数据类型,也可以自定义的数据类型,如一个自定义的POJO对象,Spring MVC会通过反射把请求中的参数设置到对象中,自动转换类型

3.3 复杂数据类型

这里指的复杂数据类型指的是一个自定义类型中还包含另外一个对象类型,如用户类型中包含产品对象:
这样就需要在传入参数的时候做法如下:?username=xxx&product.name=zzz&product.price=123
其中product是定义在用户类型中的产品类的对象。

3.4 List集合类型

不能直接在action的参数中指定List类型,定义一个类型包装List集合在其中,

//产品集合
public class ProductList {
    private List<Product> items;
    public List<Product> getItems() {
        return items;
    }
    public void setItems(List<Product> items) {
        this.items = items;
    }
}

然后在controller中

//集合类型
    @RequestMapping("/action03")
    public String action03(Model model, ProductList products) {
        model.addAttribute("message", products.getItems().get(0) + "<br/>" + products.getItems().get(1));
        return "foo/index";
    }

那么在发送请求的时候就应该按照集合的方式来发送请求:如下:
items[0].name=book&items[0].price=123

3.5 Map集合类型

Map与List的实现方式基本一样,这里先定义了一个包装Map的类型ProductMap:

/**
 * * 产品字典
 */
public class ProductMap {
    private Map<String, Product> items;
    public Map<String, Product> getItems() {
        return items;
    }
    public void setItems(Map<String, Product> items) {
        this.items = items;
    }
}
 // Map类型
    @RequestMapping("/action04")
    public String action04(Model model, ProductMap map) {
        model.addAttribute("message", map.getItems().get("p1") + "<br/>" + map.getItems().get("p2"));
        return "foo/index";
    }

然后请求参数写法:
items[p1].name=pen&items[p2].name=ball

思考题:SpringMVC的Controller是单例还是多例的?在多线程情况下

单例的,
尽量不要在controller里面去定义属性,如果在特殊情况需要定义属性的时候,那么就在类上面加上注解@Scope(“prototype”)改为多例的模式,以前struts是基于类的属性进行发的,定义属性可以整个类通用,所以默认是多例,不然多线程访问肯定是共用类里面的属性值的,肯定是不安全的,
但是springmvc是基于方法的开发,都是用形参接收值,一个方法结束参数就销毁了,多线程访问都会有一块内存空间产生,里面的参数也是不会共用的,所有springmvc默认使用了单例,所以controller里面不适合在类里面定义属性,只要controller中不定义属性,那么单例完全是安全的。
springmvc这样设计主要的原因也是为了提高程序的性能和以后程序的维护只针对业务的维护就行,要是struts的属性定义多了,都不知道哪个方法用了这个属性,对以后程序的维护还是很麻烦的。


SpringMVC接收请求参数

1.使用request封装形式

可以在Controller中的参数列表写入HttpRequest request…然后可以通过request.getParameter(“…”)获得请求参数。
但是这种方式还是比较麻烦。

2.自动封装

可以自动识别请求参数并自动封装。
在Controller中的参数列表

2.1基本类型

只要类型名字 与 请求参数的一致,那么就可以识别,并封装。

2.2封装对象

只要对象中的类型名字 与 请求参数的一致。

2.3封装复杂对象

类中有引用对象:那么前端的格式就需要变成 :<input ...name=(对象.属性)>
根据set方法来封装注入,也可以通过有参构造,但是引用对象不能通过构造方法进行封装,还是需要Set方法来对引用对象进行封装(可以结合(构造方法+Set方法)一起使用,也可以单纯用Set方法来使用)
即构造方法只能放基本类型
但是还有一个点,就是如果有构造方法的话,那么一定会生成一个bean,但是如果bean里有一个int类型的参数为null,就会报错,因为null没有办法转成int。

2.4 封装集合对象

List<Teacher> teachers
在前端的请求参数的格式需要为 name=”teachers[0].name”

3.其他补充

3.1@RequestParam:

可以放在方法的参数里,表示必须有这些参数和字段,否则报错:

void xxx(@RequestParam String username,String password){}
//请求参数中必须有username和password

void xxx(@RequestParam("username") String aaa,String password){}
//找到请求参数为username,自动转换成名为aaa的参数

3.2@PathVariable:(Restful 风格的API)

传统风格的API : localhost/user/person/username?id=123
Restful风格:由于前端很多都是手机、平板,桌面电脑等等,需要统一一个通信方式,给到同一个后端处理。所以这种风格开始产生。
通过不同的请求方式,会返回不同的数据。

@RequestMapping("/user/{id}")
public String restful(@PathVariable int id){}

@RequestMapping("/user/{id}/person/{name}")
public String restful(@PathVariable("id") int idxx,@PathVariable String name){}

3.3RequesHandler:从请求头中拿参数。

@RequestMapping("/user/{id}")
public String restful(@PathVariable int id,
              @RequestHandler("host") String host,
              @RequestHandler("Accept") String accept){}

3.4 @CookieValue:从请求参数中拿Cookie(指定name)

3.5 @SessionAttributes:

3.6 @SessionAttribute:从Session里面取东西(前提session里面有)

4.封装中的乱码问题

使用Filter req.setCaracterEncoding(“UTF-8”);//设置请求正文的解码方式(post请求方式下)
如果是get请求,默认使用UTF-8解码,所以不需要Filter

5.封装中的日期问题

自定义的日期格式转换器:
实现一个接口:Convert

public class  xxx imp Converter<String,Data>{
    @Override
    public Date convert(String o){
    SimleDateFormat formatter = new SimplerDataFormat("yyyy-MM-dd");
    Date parse = null;
    parse = formatter.parse(o);
    }
}

还要再告诉Springmvc

<mvc:annotation-driven >
<bean class="FormattingConvertsionServiceFactoryBean">
    <propery name="converters">
        <set>
            <bean class="注入的类xxx">
        </set>
    </property>
</bean>

SpringMVC的文件上传

1.1导包+配置

文件上传也是依赖apche的文件上传的依赖包:commons-fileupload / commons-io

<bean name="multipartResolver" class="CommonssMultipartResolver" /> 

1.2 代码:

//这个名字必须和表单的提交名一致,表单中 name="file"
@Request String upload(MultipartFile file){
    String originalFilename = file.getOriginalFilename();
    long size = file.getSize();
    //如果路径是这个,就是在tomcat的bin目录下,文件名就是原本的文件名
    File des = new File("temp"+originalFilename);
    file.transfer(dest);
}

//还可以同时封装文件和对象
@Request String upload(User user,MultipartFile file){}

如果要批量上传:
。。。

log4j 日志框架

3个包:log4j.jar/log4j-api.jar/log4j-core.jar

Logger logger = LogManager.getLogger(TestMain.class);(指定的类的字节码文件)

日志分级(Level)

最低级别(tarce):logger.trace()
trace ——> debug(排查错误级别) ——>info(关键信息) ——>warn(警告信息) ——> error(错误信息) ——> fatal(致命错误)

相应的配置文件:log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.org.deepsl.hrm.mapper.UserMapper=DEBUG
log4j.logger.org.deepsl.hrm.mapper.DeptMapper=DEBUG
log4j.logger.org.deepsl.hrm.mapper.EmployeeMapper=DEBUG
log4j.logger.org.deepsl.hrm.mapper.JobMapper=DEBUG
log4j.logger.org.deepsl.hrm.mapper.NoticeMapper=DEBUG
log4j.logger.org.deepsl.hrm.mapper.DocumentMapper=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

其中的

log4j.rootLogger=ERROR, stdout

可以打印ERROR以上的所有级别

多表关联

一对一(在JavaBean的层次上)

应该维护另一方的引用来表示一对一:
如果只在一方 引用另一方,单向一对一。
如果两方都有另一方的引用,双向一对一。

在配置时,如何在映射配置文件中映射这样的关系?

原本应该是:Select * from student as s inner join cellphone as c on s.sid=c.id where s.sid=#{id}

可以考虑:建立一个新类,继承其中一个类,然后将另一个类的属性放过来,然后使用这个新类作为关联查询的结果。

缺点:是一种平铺的映射,所有的数据都在其中,不存在引用的关系。

方法二:写一个resultMap

<resultMap id="studentMap" type="student">
    <id column="id" property="id" /><!--这个id主键必须要写,column表示student的某一列的名字,property表示bean类中的对应的字段,下面的result可以省略,可以根据sql语句来找-->
    <!--<result column="name" property="name" />
    <result column="chinese" property="chinese" />
    <..> 可以省略,只保留 id 标签-->
    <!-- 要进行二次查询,去关联查询cellphone,其中property表示查询哪个属性(student类中的引用对象),
    根据column去查询哪一列,查询结果是什么类型javaType-->
    <associtation property="" column="id" javaType="cellphone" select="查询的二表">
        <!-- <id column="cid" property="cid" />
        <result column="cname" property="cname" />
        可以全部省略-->
    </>
</resultMap>
<select id="查询的二表" resultType="cellphone">
    select * from cellphone where id=#{id}
</select>
<select id="..." resultMap="studentMap">

</select>

一对多

<resultMap id="studentMap" type="student">
    <id column="id" property="id">
    <collection property="cellphone" column="id" javaType="list" select="findCellphoneBysid" />

</>
<select id="findCellphoneBysid" resultType="cellhone">
    sql语句,查询多条语句
</>

多对多

需要一个关系表来表示两个表之间的多对多的关系。
只需要两个类中有对方的对象,每个对象都是一个List<>,
然后再配置映射关系,


逆向工程 Generator

去Mybatis官网的generator,里面有一套配置。

整合SSM

使用整合SSM的方式做一个人事管理项目。比较之前的商城项目,看看优缺点。

Maven

猜你喜欢

转载自blog.csdn.net/qq_38962004/article/details/80522056
今日推荐