目录
CH1 概述
框架
-表示层框架:Struts、SpringMVC
-持久层框架:MyBatis、Hibernate
-容器类:Spring、EJB
Struts2
(2)Struts2:约定优于配置 / 每个public且返回类型为String的都是一个Servlet
Maven
(3)Maven:基于项目对象模型(POM)的项目管理机制
-通过pom.xml来管理项目的构建和模块间的依赖
-通过配置来解决内部模块和外部插件的依赖关系
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
</dependencies>
文件夹
target、out:编译结果和输出结果
src:源代码
java,resource:src下的源代码,资源文件
test:测试源代码
web:html,css,JS
pom.xml:根目录下
maven项目构建和管理
compile:java源代码编译成class,放到target目录下
clean:删除target
package:生成打包文件(.jar/.war)
install:打包文件上传到本地仓库
deploy:打包文件上传到web服务器或私服
test
CH2 Spring Ioc和Bean
Ioc控制反转
-用于降低耦合度,实现方式为依赖注入
-由系统外部实体将依赖的对象注入进来(不用new)
-调用方引用一个接口或类就可以获得对象
实现基础:面向接口编程+工厂模式+依赖注入
依赖注入
1.构造注入
<constructor-arg>
<bean name="对象名" class="类从哪创建">
<constructor-arg>
<ref bean="fileHello">
</constructor-arg>
</bean>
2.设置注入
<property>
<bean id="userBiz" class="UserBizImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
3.区别
a.设置注入支持大部分依赖注入,构造注入不支持
b.设置注入不会重写构造方法的值
c.设置注入不能保证某种依赖是否已经被注入,构造注入不允许生成依赖关系不完整的对象
d.设置注入使DAO,Impl,Service,ServiceImpl,配置
applicationContext.xml实现
构造注入使用ServiceImpl,配置applicationContext.xml实现
SpringFramework
-非浸入式
1.SpringBean
-由Ioc容器初始化,装配,管理的类
-Spring框架是实例化,配置,管理Bean的容器,通过Bean工厂实现Ioc和DI
-管理分为xml和注解
·xml
作用域:singleton/prototype/request/session
a. bean的三种实例化方式:类构造器/静态工厂/实例工厂
//Bean的实例化
//...............................................................
//类构造器(class中填类名)
//如果有参需要使用<constructor-arg>
<bean id="exampleBean" class="examples.ExampleBean"/>
//...............................................................
//静态工厂
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
//
public class ClientService{
private static ClientService clientService=new ClientService();
private ClientService(){}
public static ClientService createInstance(){
//1.私有(别人不可用)
//2.对外获取方法需为静态(可以直接使用类名调用方法)
return clientService;
}
}
//...............................................................
//实例工厂
<bean id="serviceLocator" class="examples.DefaultServiceLocator" >
</bean>
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
//
public class DefaultServiceLocator(){
private static ClientService clientService=new ClientServiceImpl();
private DefaultServiceLocator(){}
public ClientService createClientServiceInstance(){
return clientService;
}
}
b. bean的依赖注入:构造注入/设置注入
//构造注入
<beans>
<bean id="Foo" class="x.y.Foo">
//Foo下有两个属性
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar">
<bean id="baz" class="x.y.Baz">
</beans>
//
package x,y;
public class Foo{
public Foo(Bar bar,Baz baz){
//...
}
}
//...............................................................
//设值注入
<bean id="exampleBean" class="examples.ExampleBean">
<property name="beanOne"><ref bean="anotherExampleBean"/>
<property name="beanTwo"><ref bean="yetAnotherBean"/>
<property name="integerProperty" value="1">
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean">
<bean id="yetAnotherBean" class="examples.YetAnotherBean">
//
public class ExampleBean(){
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne){
this.beanOne=beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo){
this.beanTwo=beanTwo;
}
public void setintegerProperty(int i){
this.i=i;
}
}
·注解
@Component :用于定义Bean
@Repository,@Service,@Controller,@Autowired
CH3 Spring AOP和实现
AOP
-AOP称为面向切面编程,可以对业务逻辑的各个部分进行隔离,使得耦合度
降低
-采取横向抽取机制解决耦合和复用问题
-OOP为从上到下的关系,AOP为左到右的关系
基本概念
-关注点:一个特定的问题
-核心关注点:完成核心业务逻辑
-横切关注点:跨越应用程序多个模块的方法或功能。例如:日志、安全、
缓存、事务
-切面:将散落的代码规整在一起的横向关系(做什么)
-连接点:在什么地方执行(何处做)
-建议(通知):切面切入代码的方式(类里的方法),分为前置、后置、
环绕、异常、引介
-目标:接口或方法,被通知的对象
-引介:为一个类或接口添加方法或字段
-织入:把切面应用到目标对象并创建新的代理对象的过程
实现技术
AOP实现原理:
a.JAVASE 动态代理:
基于接口:JDK代理一定基于接口,会生成目标对象的接口的
子对象。
基于类(cglib):
需要Proxy,InvocationHandler(自动生成代理类)
参考文章:spring-aop切面知识
public interface InvocationHandler {
public Object invoke(Object proxy,Method method, Object[] args)throws Throwable;
}
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException
b.动态字节码生成
c.JAVA代码生成
*1.动态代理实现AOP
a.代理解决的问题:当两个类需要通信时,引入第三方代理类,将两个类的关系解耦
b.代理的完成与另一个类之间的关系的统一管理
c.代理类和委托类要实现相同的接口(代理调用的是委托类的方法),且一个动态代理可以代理多个类,只要实现的是同一个接口
d.一个动态代理类代理的是一个接口,一般就是对应一类业务
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态:在程序运行时运用反射机制动态创建而成。
//接口
public interface UserDAO{
void save();
void update();
void delete();
void find();
}
//...............................................................
//实现类
public class UserDAOImpl implements UserDAO{
@Override
public void save(){
System.out.println("新增用户");
}
@Override
public void update(){
System.out.println("修改用户");
}
@Override
public void delete(){
System.out.println("删除用户");
}
@Override
public void find(){
System.out.println("查询用户");
}
}
//代理类
public class MyJDKProxy implements InvocationHandler{
/*用这个类自动生成代理类*/
private Object proxyObject; //代理谁?
/*被代理的接口*/
public MyJDKProxy(Object proxyObject){
this.proxyObject=proxyObject;
}
/*生成得到代理类*/
public static Object bind(Object object){
//这里的Class为类加载器
Class class1=object.getClass();
/*
Proxy.newProxyInstance(类加载器,要代理哪个类,
MyJDKProxy用InvocationHandler)
*/
return Proxy.newProxyInstance(class1.getClassLoader(),
class.getInterfaces(),
new MyJDKProxy(Object)
);
}
/*处理代理实例并返回结果*/
@Override
public Object invoke(Object proxy,Method method,
Object[] args)throws Throwable{
//前置:业务方法之前所要进行的操作;
if(!method.getName().equalsIgnoreCase("find")){
beforeAdvice();
}
//使用invoke来执行接口下的方法
Object object=method.invoke(proxyObject,args);
afterAdvice();//后置,业务方法执行完之后想要返回什么信息;
return Object;
}
public void afterAdvice(){
System.out.println("后置建议:日志记录.......");
}
public void beforeAdvice(){
System.out.println("前置建议:安全授权.......");
}
public static void main(String[] args){
//bind用于生成得到代理类
UserDAO userDAO=(UserDAO)MyJDKProxy.bind(new UserDaoImpl());
userDAO.save();
userDAO.delete();
userDAO.upadte();
userDAO.find();
}
2.AspectJ实现AOP
import j2se.BusinessLogic;
//定义一个切面
public aspect SecurityAspect{
//连接点
private pointcut securityExecution():
//切入businessMethod1()
//execution:在方法执行时触发
execution(public void BusinessLogic.businessMethod1());
before():securityExecution(){
doSecurityCheck();
}
private void doSecurityCheck(){
System.out.println("Doing Security Check");
}
}
//编写事务管理切面
public aspect TransactionAspect{
private pointcut transacitonExecution();
execution(public void BusinessLogic.businessLogic.businessMethod1())||execution(public void BusinessLogic.businessLogic.businessMethod2())
}
//织入
public class Test{
public static void main (String[] args){
BusinessLogic business=new BusinessLogicCoreConcern();
business.businessMethod1();
business.businessMethod2();
}
}
3.AspectJ横切技术
a.动态横切:通过切入点和连接点在一个切面创建行为(代表AOP)
ex.添加安全验证、日志记录
b.静态横切:把扩展和实现附加到对象的基本结构中(代表引入/混入)
AspectJ实现静态横切
//新功能接口
public interface IsValidatable{
public boolean isOnSale();
}
//把功能引入到Product
public aspect ProductValidateAspect{
declare parents:Product extends IsValidatable;
public boolean Product.isOnSale(){
return this.getPrice()>0?true:false;
}
}
public test{
public static void main(String[] args){
Product product = new Product();
if(product.isOnSale()){
System.out.println("商品代售");
}
}
}
Spring框架中AOP的实现
a. 传统Spring AOP
b.基于AspectJ的Spring AOP
配置:
<app:config>
<!--配置切入点-->
<aop:pointcut id="pointcut1" expression="execution(*完整类名.方法名)"/>
<!--配置切面-->
<aop:aspect ref="myAspectXml">
<aop:before method="afterrunning" piont-cut-ref="pointcut1" returning="result"/>
</aop:aspect>
</app:config>
注解:@Aspect
CH4 Spring MVC入门
Spring MVC简介
-Spring体系中的轻量级WebMVC(表示层)框架
-核心为Controller
-基于SpringIoc容器运行,所有对象被Ioc容器管理
Spring MVC基本使用
配置Maven
在pom.xml配置
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
配置web.xml
打注解@Servlet或配置DispatcherServlet
<!--配置DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置applicationContext.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!--自动创建springIoc容器-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<url-pattern>2.1.4</version>
</servlet>
配置applicationContext.xml
以下为配置web.xml后的配置
<beans xmlns="http://www.springframework.org/schemas//beans">
...
</beans>
编写Controller
a.请求接收参数
@RequestMapping:不区分请求方法
@GetMapping
@PostMapping
b. 响应处理
@ResponseBody:打在类上,以字符串内容响应
ModelAndView:
ModelAndView view=new ModelAndView();
if(account!=null){
view.setViewName("main.jsp");
view.addObject("loginAccount",account);
return view;
}
@Controller
@RequestMapping("/hello")//配置URL映射
public class DemoCnotroller{
@Autowired
private AccountService accountService;
@SessionAttributes(value = "account",types = {Account.class})//获取account
@GetMapping("/loginForm")//配置URL映射
public String Hello(@SessionAttribute("account") Account account,
@RequestParam(value = "firstName",required = false) String firstName,)
{
//@RequestParam为从页面上提交的值,如果前后端属性名要一致,则不用打@RequestParam
Account account1=new Account();
account1.setUsername(account.username);
return "Hello,"+account.username+",your firstname is:"+firstName;
}
}
Thymeleaf的使用
-数据和HTML分离
@PostMapping("/login")
public String login(Account account,Model model){
if(service.login(account)!=null){
return "success";
}
else{
model.addAttribute("message","用户名或密码错误");
return "login";
}
}
<p th:text="${message}">Hello World!</p>
CH5 Spring MVC进阶
Spring MVC拦截器
-通过HandlerInterceptor实现接口拦截器
preHandle:请求到达前置处理方法
postHandle:请求处理后置处理方法
afterCompletion:响应处理后置处理
SpringBoot
-为简化Spring框架的使用退出的工具
1.主要功能
-SpringBootStarter:将常用的依赖合并到一个依赖里
-自动配置:配置bean
2.特点
-约定优于配置
-自动发现、自动装配
3.使用SpringBoot
-入口类(***Application)
-@SpringBootApplication
package org.csu.mypetstore;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("org.csu.mypetstore.persistence")
public class MypetstoreApplication {
public static void main(String[] args) {
SpringApplication.run(MypetstoreApplication.class, args);
}
}
-application.properties
#tomcat服务器使用的端口
server.port=80
#Spring JDBC数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/mypetstore
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=密码
#mybatis配置
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
mybatis.type-aliases-package=org.csu.mypetstore.domain
mybatis.configuration.lazy-loading-enabled=false
#项目运行时在控制台输出sql语句等信息
logging.level.org.csu.mypetstore.persistence=debug
CH6 Spring 事务和JDBC模板
JPA和传统JDBC
1.JPA(持久层API)
JPA:持久层API,早期JPA即JDBC(包括Connection/Statement/ResultSet等接口),目前JPA是指用于完成ORM的注解
JPA规范:
·ORM映射元数据(XML和注解)
·JAVA调用的API接口
·面向对象查询语言JPQL
2.传统JDBC
DBUtil
public class DBUtil{
private static String driver="com.mysql.jdbc.Driver";
private static string url="jdbc:mysql://127.0.0.1:3306/demo";
private static string username=" ";
private static string password=" ";
public static Connection getConnection() throws Exception{
...
}
...
}
新增用户
public boolean insertUser(User user)throws Exception{
Connection connection=DBUtil.getConnection();
String inserUserSQL="INSERT INTO userinfo VALUES(?,?)";
PreparedStatement pStatement=connection.prepareStatement(inserUserSQL);
pStatement.setInt(1,user.getId());
...
//如果有返回结果
//ResultSet resultSet=statement.executeQuery(SQL语句)
//DBUtil.closeResultSet(resultSet);
DBUtil.closeStatement(pStatement);
DBUtil.closeConnection(connection);
}
3.Spring JDBC 模板
UserDAO
public interface UserDAO{
private static String inserUserSQL="INSERT INTO userinfo VALUES(?,?)";
...
public boolean insertUser(User user);
...
}
UserDAOImpl
jdbcTemplate.update(insertUserSQL,user.getName(),user.getAge());
UserRowMapper
public class UserRowMapper implements RowMapper<User>{
public User mapRow(ResultSet resultSet int i){
User user=new User();
user.setId(resultSet.getString("name"));
...
}
}
4. 数据库事务
-一个程序执行单元的一系列操作,要么完全执行,要么完全不执行
5.事务的特性
原子性:一个事务的不可分割的
一致性:如果被保存在多个地方,各个地方都需要一致
隔离性:事务的执行不能被其它事务干扰
持久性:事务一旦提交,对数据库的改变是永久的
6.数据库事务常见的并发问题
脏读:一个事务读到了另一个事务未提交的数据(事务回滚造成)
不可重复读:一个事务读到另一个事务已经提交的update数据导致多次查询结果不一致
幻读:一个事务读到另一个事务已经提交的insert数据导致多次查询结果不一致
7.事务隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 (能够读取到没有被提交的数据) | 是 | 是 | 是 |
读已提交(能够读到那些已经提交的数据) | 否 | 是 | 是 |
可重复读(读取了一条数据,这个事务不结束,别的事务就不可以改这条记录) | 否 | 否 | 是 |
串行化(不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务) | 否 | 否 | 否 |
8.JDBC事务处理
setAutoCommit:是否自动提交
commit:显示提交事务
rollback:回滚
Connection connection=DBUtil.getConnection();
connection.setAutoCommit(false);
connection.commit();
Spring事务管理(Transaction)
1.编程式事务
编程式事务方式需要是开发者在代码中手动的管理事务的开启、提交、回滚等操作
a.基于底层API的编程式事务管理
PlatformTransactionManager:将事务管理委托给Hibernate或iBatisTransactionDefinition,TransactionStatus
@Service
public class OrderServiceImpl implements OrderService{
@Autowired
private OrderDAO orderDao;
@Autowired
private ProductDAO productDAO;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
public void addOrder(Order order){
//工厂产生事务处理对象
TransactionStatus transactionStatus=transactionManager.getTransaction(transactionDefinition);
try{
orderDao.insert(order);
Product product=productDao.select(order.getProductsId());
}
catch(Exception e){
transactionManager.rollback(transactionStatus);
}
}
}
b.基于模板的编程式事务管理
TransactionTemplate
public class OrderServiceImpl implements OrderService{
@Autowired
private OrderDAO orderDao;
@Autowired
private ProductDAO productDAO;
@Autowired
private TransactionTemplate transactionTemplate;
public void addOrder(Order order){
TransactionTemplate.execute(new TransactionCallBack(){
//内部类
public Object doInTransaction(TransactionStatus transactionStatus){
try{
orderDao.insert(order);
Product product=productDao.select(order.getProductsId());
}
catch(Exception e){
transactionManager.rollback(transactionStatus);
}
return null;
}
});
}
*2.声明式事务
声明式事务管理方法允许开发者配置的帮助下来管理事务,而不需要依赖底层API进行硬编码。开发者可以只使用注解或基于配置的 XML 来管理事务。
-对方法进行拦截,目的是解耦合
-四种声明式事务的实现:
·TransactionInterceptor拦截器
<bean id="orderServiceTarget" class="org.csu.demo.service.impl.OrderServiceImpl"/>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"/>
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="select*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
·TransactionProxyFactoryBean代理
<!--和TransactionInterceptor的区别-->
<bean id="orderServiceTarget" class="org.csu.demo.service.impl.OrderServiceImpl"/>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"/>
·<tx>命名空间
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<th:method name="get*" propagation="REQUIRED" read-only="true"/>
<th:method name="find*" propagation="REQUIRED" read-only="true"/>
<th:method name="search*" propagation="REQUIRED" read-only="true"/>
<th:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(*org.csu.demo.service.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
·@Transactional注解
@Transactional(propagation=Propagation.REQUIRED)
1.编程式事务和声明式事务的区别
编程式事务方式需要是开发者在代码中手动的管理事务的开启、提交、回滚等操作
声明式事务管理方法允许开发者可以只使用注解或基于配置的 XML 来管理事务。
声明式事务(优):
- 声明式事务管理使用了 AOP 实现,对代码没有侵入性,方法内只需要写业务逻辑就可以。
声明式事务(缺):
- 最小粒度要作用在方法上。
- 声明式事务容易失效。比如:数据库引擎不支持事务、@Transactional 应用在非 public 修饰的方法上、同一个类中方法调用,导致@Transactional失效。
CH7 ORM概述和MyBatis入门
持久化
-瞬时、临时状态:保存在内存的数据,程序退出就消失
-持久化:在瞬时状态和持久状态之间
-持久状态:保存在磁盘上的程序数据
-脱管、游离状态:session被关闭,对象就成为脱管的。如果再次连接到session,又会变回持久的。脱管期间的改动会保存到数据库
ORM
-对象关系映射,完成瞬态的对象数据到持久的关系型数据映射的机制
MyBatis(半自动)
使用XML或注解用于配置和原始映射,将Java POJOs映射成数据库中的记录。运行时,占位符被指定的参数值取代,参数可能来自参数映射表、JavaBean属性、参数对象
Configuration.xml:导入依赖
<environment> <mappers>
<environments default="demo">
<environment id="demo">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mypetstore"/>
<property name="username" value=" "/>
<property name="password" value=" "/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/demo/domain/UserMapper.xml"/>
</mappers>
SqlSessionFactory
public class SessionFactoryUtil{
private static String source="com/demo/dao/Configuration.xml";
public static SqlSessionFactory getSqlSessionFactory(){
SqlSessionFactory sqlSessionFactory=null;
try{
Reader reader=Resource.getResourceAsReader(source);
sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
}catch(IOException e){
e.printStackTrace();
}
return sqlSessionFactory;
}
MyBatis实现CRUD(基于SqlSessionFactory)
sqlSessionFactory=SessionFactoryUtil.getSqlSessionFactory();
session=sqlSessionFactory.openSession();
result=session.selectOne("com.demo.dao.UserDAO.find",user);
UserMapper.xml:映射器
<mapper namespace="org.csu.mypetstore.persistence.AccountMapper">
<select id="getAccountByUsernameAndPassword" parameterType="Account" resultType="Account">
SELECT USERID AS username,EMAIL AS email,FIRSTNAME AS firstName,
LASTNAME AS lastName,STATUS,ADDR1 AS address1,ADDR2 AS address2,
CITY AS city,STATE,ZIP,COUNTRY,PHONE
FROM ACCOUNT WHERE USERID =#{username}
</select>
</mapper>
Hibernate(全自动)
- 把通过SQL语句进行持久化改为直接操作对象
- 根据持久化对象的状态生成SQL语句
- 用Session维持持久化对象
- 使用反射、事务、缓存
<hibernate-mapping package="org.hibernate.tutorial.domain">
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>
</hibernate-mapping>
private void createAndStoreEvent(String title,Date theDate){
Session session=HibernateUtil.getSessionFactory().getCurrentSession();
Event theEvent=new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
//缓存,当使用了commit会对数据库数据修改
session.getTransacation().commit();
}
Mybatis和Hibernate的区别
Mybatis(优):
- sql写在xml里,便于统一管理和优化, 解除sql与程序代码的耦合
- 提供xml标签,支持编写动态sql
- 速度相对于Hibernate的速度较快
Mybatis(缺):
- 关联表多时,字段多的时候,sql工作量很大
- sql依赖于数据库,导致数据库移植性差
- 编写动态sql时,不方便调试,尤其逻辑复杂时
- 提供的写动态sql的xml标签功能简单
- 由于xml里标签id必须唯一,导致DAO中方法不支持方法重载
- 对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql
Hibernate(优):
- 数据库移植性良好
- 拥有完整的日志系统
- 可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。
Hibernate(缺):
- 灵活度上hibernate不及mybatis。
- 学习门槛不低,精通门槛更高
CH8 Mybatis进阶
- 将Configuration.xml的内容整合到application.properties
- 使用Spring IoC 容器管理sqlSessionFactiory 和SqlSession
- 使用面向接口+Mapper配置实现DAO
Configuration(application.properties)
Configuration的配置内容
- environments:环境配置
- mappers:映射器
- typeAliases:类型别名
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
</typeAliases>
mabatis.type-aliases-package=org.csu.demo.domain
settings:设置全局特征(缓存延迟、延迟加载)
mybatis.cofiguration.lazy-loading-enable=true
typeHandlers:类型处理器(数据库中的类型和JAVA数据类型不匹配)
mybatis.type-handlers-package=org.csu.demo.persistence.util
Mapper
1.CRUD
<!--当返回结果有多行时,可以使用hashmap解决类型问题-->
<!--resultType="hashmap"-->
<!--resultType:可一一对应,resultMap:无法一一对应-->
<select id="addAccount" parameterType="String">
INSERT INTO SIGNON (USERNAME,PASSWORD)
VALUES(#{username}, #{password})
</select>
2.参数类型指定
-当参数类型和数据库字段类型不匹配
#{property,javaType=int,jdbcType=NUMERIC}
CRUD
动态SQL语句
-if,choose(when,otherwise),trim(where,set),foreach
if
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT* FROM BLOG WHERE state = 'ACTIVE'
<if test="title!=null">
AND title like #{title}
</if>
</select>
choose(when,otherwise)
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT* FROM BLOG WHERE state = 'ACTIVE'
<choose>
<when test="title!=null">
AND title like #{title}
</when>
<when test="author!=null and author.name!=null">
...
</when>
<otherwise>
...
</otherwise>
</choose>
</select>
trim(where,set)
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT* FROM BLOG WHERE
<if test="title!=null">
AND title like #{title}
</if>
<if test="state!=null">
...
</if>
</select>
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT* FROM BLOG
<where>
<if test="title!=null">
AND title like #{title}
</if>
<if test="state!=null">
...
</if>
</where>
</select>
foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT * FROM POST P WHERE ID in
<foreach item="item" index="index" collection="list" open="(" seperator=" " close=")">
#{item}
</foreach>
</select>
MyBatis 三剑客
Mybatis-generator:自动生成javabean和Mapper
<sqlMapGenerator targetPackage="mappers" targetProject="./src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
- Mybatis plugin:Mapper接口到映射器的自动导航
- Mybatis PageHelper:查询数据量大的数据库表时,减少数据库查询压力,降低客户端的数据加载量
- Mybatis plus:包含三剑客的功能
public void addAccount(String username,String password){
accountMapper.addAccount(username,password);
}
public List<Item> getItemListByProduct(String productId){
return itemMapper.getItemListByProduct(productId);
}
CH9 前后端分离
前后端分离概述
Web 开发模式
早期时代(Servlet=JSP)
- 不适合复杂的业务逻辑
- 可维护性差
- 协作性差
- 难有交互
MVC时代
- 可维护性好转
- 前后端分工,但是分工不明确
- 前端重度依赖后端,体验不好
AJAX时代(HTML:WEB服务器、CSS,JS:应用服务器)
- 前后端分工清晰
- 前端不再强依赖后端
- 请求由程序员用AJAX发出
-
WEB服务器(CDN):安装了Web服务器应用的物理主机
功能:处理HTTP协议栈、文件映射系统、IO、 多线程处理请求/响应、日志记录、代理服务
ex.Apache,Nginx -
应用服务器:能编译、运行JavaWeb业务代码的服务器
ex.Tomcat(WEB服务器+WEB容器)、JBoss -
CDN:内容分发网络
功能:降低网络拥塞
技术:内容存储、分发技术
会将前端静态页面采用CDN进行部署
SPA时代
-
前端采用MVC
-
可维护性提高
-
前后端接口约定很重要,前端复杂度控制
-
第一次前后端分离(MVVM):
(1)前端:浏览器跑大量JS代码,效率无法提高、接收数据,返回数据、处理渲染逻辑
(2) 后端:提供数据、处理业务逻辑、代码跑在服务器上
(3)问题:分工、性能(浏览器性能)、重用、跨终端(需要重复实现) -
第二次前后端分离:
后端 | 前端 | 前端 |
---|---|---|
X | 服务器 | 浏览器 |
JAVA | NodeJS | JS+HTML+CSS |
服务层、提供数据接口、封装业务逻辑 | 跑在服务器上的JS、转发数据、路由设计、渲染页面 | 跑在浏览器上的JS、JS/CSS加载和运行、DOM操作 |
- NodeJS:服务端JS(JavaScript运行环境),让JS跑在服务器中,提高前端性能。(把操作DOM的功能去除,保留xhr语法支持和优化)
前后端分离技术(RESTful API)
-
资源:网络上的一个实体,或者说是网络上的一个具体信息
-
表现层:把”资源”具体呈现出来的形式
-
状态转化:如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”。而这种转化是建立在表现层之上的,所以就是”表现层状态转化”。
GET:获取资源
POST:新建资源(也可以用于更新资源)
PUT:更新资源
DELETE:删除资源。
R:网络资源/资源的表述(资源呈现出来的形式)
ST:状态转换(用户访问资源)
状态转换建立在资源表述的基础上,称为REST(思想)。符合REST设计原则的称为Restful框架。
一个资源的状态改变,应该由客户端控制。服务端只提供资源。
什么是RESTful API
RESTful API是目前前后端分离的最佳实践,它是一套标准,一个规范,而不是一个框架。 如果我们在设计的时候依据RESTful准则,那么这套API这可以称作RESTful API。
特点:
-
RESTful API轻量,直接通过http或者https,不需要额外的协议,一般有四种常用操作post/get/put/delete。
-
RESTful API面向资源,一目了然,具有自解释性。
-
RESTful API数据的描述十分简单,基于Json或XML的数据格式进行数据传输。
-
每一个URI代表一种资源
REST六大约束
- 客户端-服务器:关注点分离、提高服务端简单性、客户端可移植性
- 无状态协议:会话信息保存在客户端,请求要包含所有信息,提升服务端简单性、可靠性、可见性
- 缓存:响应内容应说明可缓存/不可缓存,减少客户端和服务端交互次数
- 分层系统:将服务器端的安全、负载均衡、错误处理实现细节隐藏
- 按需代码:客户端可以直接从服务端下载部分代码,简化客户端
- 统一接口:所有接口统一使用restful设计思想
(1)资源标识:服务器任何资源都应通过UPI进行唯一的标识
(2)通过表示操作资源:客户端持有资源的表述,就应有足够的信息对资源进行操作
(3)响应的自描述性:每条响应消息都应该包含足够的信息来描述如何处理本条响应
(4)超媒体作为应用程序状态的引擎:点击连接跳转到另一个资源(REST API)
ex. 服务端只提供/order,如果要获取信息,用get()、新建表单用post()。接口做什么根据请求不同而变化。
RESTful API请求设计规范
- URL使用名词,且尽量是负数
- URL使用嵌套表示关联关系
- 使用正确的HTTP方法(GET \ POST \ PUT \ PATCH \ DELETE)
- 不符合CRUD的,使用POST+动词 \ 查询字段带action \ 设计子字段
RESTful API响应设计规范(过滤信息)
(1)GET方法的查询请求应提供信息过滤功能
- ?limit=10:返回记录的数量
- ?offset=10:返回记录的开始位置
- ?page=2&per_page=100:指定第几页,以及每页的记录数
- ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- ?animal_type_id=1:指定筛选条件
(2)状态码
- 200:成功
- 201:新建或修改数据成功
- 202:请求已经进入后台
- 204:删除数据成功
- 400:发出的请求有错误,服务器没有新建或修改数据
- 401:用户没有权限
- 403:用户得到授权,但访问被禁止
- 404:请求的是不存在的记录
- 406:请求的格式不可得
- 410:请求的资源被永久删除,不会再得到
- 422:创建一个对象时,发生一个验证错误
- 500:服务器发生错误,无法判断请求成功或失败
错误信息
{
error: "Invalid API key"
}
返回结果
·GET /collection:返回资源对象的列表
·GET /collection/resource:返回单个资源对象
·POST/collection:返回新生成的资源对象
·PUT/collection/resource:返回完整的资源对象
·PATCH/collection/resource:返回完整的资源对象
·DELETE/collection/resource:返回一个空文档
超链接
响应内容中最好包含其它相关API的连接
{
"current_user_url": "https://api.github.com/user",
"authorizations_url":"...",
}
{
"message": "Requires authentication",
"documentation_url":"..."
}
其它
- 使用HTTPs协议,而不是HTTP协议(HTTP不安全)
- 使用标准的身份认证和鉴权规范(如OAuth)
- 返回数据使用JSON而不是XML(解析XML对客户端要求较高)
请求数据的获取
@RequestParam:获取URL中的查询参数值
@RequestBody:获取POST或PUT中对象的值
@PathVariable:获取URL中的参数值
示例
<<interface>>Resource
GET
PUT
POST
DELETE
/orders
GET-list all orders
PUT-unused
POST-add a new order
DELETE-unused
/orders/{id}
GET-get order details
PUT-update order
POST-add item
DELETE-cancel item
...
CH10 SpringBoot实现RESTful API
RESTful API请求设计规范
- URL使用名词+复数
- URL嵌套表示关联关系
- 使用正确的HTTP方法
- 不符合CRUD的方法,使用
(1)POST+动词、
(2) 查询字符串中带action字段
(3) 设计子资源
Controller的请求和URL重构
@RequestParam:获取注解URL中的查询参数值
@RequestBody:获取 POST 或 PUT 的值,接收的参数是来自请求体中
@PathVariable:获取URL中的参数值
@RequestParam
http://localhost:8081/spring-boot-study/novel/findByAuthorAndType?author=唐家三少&type=已完结
@RequestParam(value = "author",required = false) String author,
@RequestParam(value = "type",required = false) String type,
@RequestBody
url = “${ctx}/main/mm/am/edit?Id=${Id}&name=${name}”
@RequestMapping("/edit")
public String edit(Model model, @RequestParam Map<String, Object> paramMap ) {
long id = Long.parseLong(paramMap.get("id").toString());
String name = paramMap.get("name").toString;
return page("edit");
}
@PathVariable
url = “${ctx}/main/mm/am/edit/${Id}/${name}”
@RequestMapping("/edit/{id}/{name}")
public String edit(Model model, @PathVariable long id,@PathVariable String name) {
return page("edit");
}
参考:@RequestParam和@RequestBody和@PathVariable用法小结
参数校验
@Validation和@Valid
@Valid
public class Admin{
@NotNull(message = "ID不可为空")
private int id;
}
Restful API的响应
基本要求:状态码、错误码、数据封装
- 要求API的响应均要序列化为JSON(fastjson、jakson、Gson)
- SpringBoot在Controller里加上@ResponseBody自动完成序列化
- 使用统一响应
{
“status": 500,
"code": 50010,
"message" :"当前登入用户过多"
"datas":{
"account":
{
"username": "admin",
"email":"admin@csu.edu.cn",
"age":18
}
}
}
统一响应
@Getter
//序列化时空的数据不会被包含
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CommonResponse<T> implements Serializable {
private int status;
private String msg;
private T data;
private CommonResponse(int status){
this.status=status;
}
}
VO\DTO\DO\DAO\PO
VO:返回给前端显示的对象
DTO:数据传输对象,提高传输效率和隐藏实现
DO:领域对象,和数据库中的表基本对应
PO:持久化对象,和数据库的表完全对应
DAO
全局异常处理
-使用@ControllerAdvice和@ExceptionHandler
- @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
- @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开;其是对Controller层进行拦截
@ControllerAdvice
public class GlobalExceptionAdvice{
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ServerResponse<ExceptionResponse> handleException(Exception e){
ServerResponse<ExceptionResponse> serverResponse = new ServerResponse<>();
return serverResponse;
}
前后端分离后的用户认证和鉴权
1.认证
前后端未分离架构中的会话跟踪:
- Cookie、Session
分离后:
- 浏览器无法自动使用Cookie、跨域问题
- Session不便存储
- Token机制
(1)Token机制
- token:指在web应用中访问资源的凭据
- 和session的区别在于服务器是否存储
- 客户端带上token,服务器根据token进行用户认证
JWT(Spring Security)
- 采用数字签名,可以被验证和信赖。且可以对数字签名进行加密。由Header、Payload和Signature组成。
- 可以用于认证和数据传输,减少对服务器的访问
- 出发后无法废止或修改
- 防止泄露应设置尽可能短的有效时间
- 采用HTTPS协议传输
Header:用于描述JWT自身的元数据,包括alg(签名的算法)、typ(token的类型)
Payload:用于存放实际需要传递的数据,包括标准字段、自定义字段、官方字段
·官方字段包含iss签发人、exp过期时间、sub主题、aud使用者、nbf生效时间、iat签发时间、jti唯一编号
Signature:对上述两部分的签名(HMAC SHA256),防止数据篡改
2.鉴权
- 授权,用户访问API的权限问题,主要用于第三方登录
- OAuth
- Spring Security OAuth2
(2)OAuth2
四种授权方式:
- 授权码
- 隐藏式
- 密码式
- 客户端凭证
CH11 前端相关技术
前端技术
1.跨域问题
同源策略:规范两个URL如果协议、主机名、端口均一致则同源。
ex. http://www.csu.edu.cn/ cs/index.html
跨域问题:浏览器不允许访问不同源的资源
解决:
- 跨域资源共享(@CrossOrigin)
- JSONP
- 反向代理
Node.js
JavaScript运行环境(解释器)
没有BOM和DOM,主要提供服务器级别的API操作(文件读写、网络服务的构建、网络通信、http服务器)
-
DOM 是为了操作文档出现的 API,BOM 是为了操作浏览器出现的 API
-
DOM 是 W3C 的标准, BOM 是 各个浏览器厂商根据 DOM在各自浏览器上的实现
特性包括事件驱动、非阻塞I/O
var http = require('http')
var server = http.createServer()
server.on('request',function () {
console.log('收到客户端的请求了')
})
server.listen(3000,function (){
console.log('服务器启动完成,可以通过http://127.0.0.1:3000/ 来进行访问')
})
ES5、ES6(ECMA Script)
- 关键字:let、const
- 模板字符串
- 简化对象和函数、函数扩展(匿名函数、形参默认值)
- Promise对象和async函数处理异步请求回调
class Person{
constructor (name,age){
this.name=name;
this.age=age;
}
showName(){
console.log(this.name);
}
}
let person = new Person('000',18);
console.log(person);
person.showName();
Webpack
模块打包程序,找到JS模块以及其它浏览器不能直接运行的拓展语言(Scss,TypeScript),转换和打包为合格的格式供浏览器使用
<script src="bundle.js"></script>
//greeter.js
module.exports = function(){
var greet = document.createElement('div');
greet.textContent ="Hi there and greetings!";
return greet ;
};
//main.js
const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeyer());
//配置文件
module.exports = {
entry: __dirname + "/app/main.js",
output: {
path: __dirname + "public",
filename: "bundle.js"
}
}
CH12 前端库和框架
基本概念
UI框架
- Layui
- Bootstrap
- iView
- Ant Design
JavaScript库和框架
- AngularJS
用于MVC,包括View、Model、Template、Scope和数据绑定
Scope&View
Scope
invoice:
new InvoiceController
View
<div ng-controller="InvoiceController as invoice">
<input ng-model="invoice.qty">
<input ng-model="invoice.cost">
{{invoice.total('USD')}}
<button ng-click="invoice.pay()">
</div>
Controller
function InvoiceController{
this.pay=function...
this.total=function...
this.cost=2.5;
this.qty=1;
}
Template,Controller,Service
Template
//index.html
<html ng=app="invoice>
<div ng-controller="InvoiceController as invoice">
Controller
//invoice .js
angular.module("invoice",["finance"]).controller(
"InvoiceController",["currencyConverter",
function(currencyConverter){}
]
);
Service
//finance.js
angular.module("fianance", []).factory(
"currencyConverter",function(){
}
};
- React
虚拟DOM、声明式、组件化
JSX:React通过将标记与逻辑共同存放在称之为“组件”的松散耦合单元,来实现关注点分离
(1)React发明了JSX,利用HTML语法来创建虚拟DOM
(2)React的核心机制之一就是可以在内存中创建虚拟的DOM元素,以此来减少对实际DOM的操作从而提升性能
(3)JSX 即Javascript XML,JSX其实就是JavaScript。当遇到<,JSX就当HTML解析,遇到 { 就当JavaScript解析。
参考:React之JSX语法
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
props传递数据
function Welcome(props){
return <h1>Hello, {props.name}</h1>
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
class ShoppingList extends React.Component{
render(){
return(
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}
- Vue
用于构建用户界面的渐进式框架,只关注视图层,可和第三方库或既有项目整合
<div id="app">
{{message}}
</div>
------------------------------
var app=new Vue({
el:'#app',
data:{
message:'Hello Vue!'
}
})
<div id="app-3">
<p v-if="seen">现在你看到我了</p>
</div>
------------------------------
var app=new Vue({
el:'#app-3',
data:{
seen:true
}
})
<div id="app">
<div>
<input v-model="title">
<button v-on:click="add">submit</button>
</div>
<ul>
<li v-for="item in list">{{item}}</li>
</ul>
</div>
--------------------------------------------------
var vm=new Vue({
el:'#app',
data:data,
methods:{
add: function(){
this.list.push(this.title)
this.title=''
}
})
--------------------------------------------------
var data={
title='',
list:[]
}
- Angular
组件化、单页应用框架,使用TypeScript语法
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + 'details'">
{{product.name}}
</a>
</h3>
<p *ngIf="product.description">
Description: {{ product.description }}
</p>
<button (click)="share()">
Share
</button>
</div>