-表示层框架:Struts、SpringMVC
-持久层框架:MyBatis、Hibernate
-容器类:Spring、EJB
(2)Struts2:约定优于配置 / 每个public且返回类型为String的都是一个Servlet
(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:根目录下
compile:java源代码编译成class,放到target目录下
clean:删除target
package:生成打包文件(.jar/.war)
install:打包文件上传到本地仓库
deploy:打包文件上传到web服务器或私服
test
-用于降低耦合度,实现方式为依赖注入
-由系统外部实体将依赖的对象注入进来(不用new)
-调用方引用一个接口或类就可以获得对象
实现基础:面向接口编程+工厂模式+依赖注入
<bean name="对象名" class="类从哪创建">
<constructor-arg>
<ref bean="fileHello">
</constructor-arg>
</bean>
<bean id="userBiz" class="UserBizImpl">
<property name="userDAO" ref="userDAO"/>
</bean>
a.设置注入支持大部分依赖注入,构造注入不支持
b.设置注入不会重写构造方法的值
c.设置注入不能保证某种依赖是否已经被注入,构造注入不允许生成依赖关系不完整的对象
d.设置注入使DAO,Impl,Service,ServiceImpl,配置
applicationContext.xml实现
构造注入使用ServiceImpl,配置applicationContext.xml实现
-非浸入式
-由Ioc容器初始化,装配,管理的类
-Spring框架是实例化,配置,管理Bean的容器,通过Bean工厂实现Ioc和DI
-管理分为xml和注解
·xml
作用域:singleton/prototype/request/session
a. bean的三种实例化方式:类构造器/静态工厂/实例工厂
//Bean的实例化
//...............................................................
//类构造器(class中填类名)
//如果有参需要使用
<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
-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代码生成
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();
}
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();
}
}
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("商品代售");
}
}
}
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
-Spring体系中的轻量级WebMVC(表示层)框架
-核心为Controller
-基于SpringIoc容器运行,所有对象被Ioc容器管理
在pom.xml配置
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
打注解@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>
以下为配置web.xml后的配置
<beans xmlns="http://www.springframework.org/schemas//beans">
...
</beans>
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;
}
}
-数据和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>
-通过HandlerInterceptor实现接口拦截器
preHandle:请求到达前置处理方法
postHandle:请求处理后置处理方法
afterCompletion:响应处理后置处理
-为简化Spring框架的使用退出的工具
-SpringBootStarter:将常用的依赖合并到一个依赖里
-自动配置:配置bean
-约定优于配置
-自动发现、自动装配
-入口类(***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
JPA:持久层API,早期JPA即JDBC(包括Connection/Statement/ResultSet等接口),目前JPA是指用于完成ORM的注解
JPA规范:
·ORM映射元数据(XML和注解)
·JAVA调用的API接口
·面向对象查询语言JPQL
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);
}
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"));
...
}
}
-一个程序执行单元的一系列操作,要么完全执行,要么完全不执行
原子性:一个事务的不可分割的
一致性:如果被保存在多个地方,各个地方都需要一致
隔离性:事务的执行不能被其它事务干扰
持久性:事务一旦提交,对数据库的改变是永久的
脏读:一个事务读到了另一个事务未提交的数据(事务回滚造成)
不可重复读:一个事务读到另一个事务已经提交的update数据导致多次查询结果不一致
幻读:一个事务读到另一个事务已经提交的insert数据导致多次查询结果不一致
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 (能够读取到没有被提交的数据) | 是 | 是 | 是 |
读已提交(能够读到那些已经提交的数据) | 否 | 是 | 是 |
可重复读(读取了一条数据,这个事务不结束,别的事务就不可以改这条记录) | 否 | 否 | 是 |
串行化(不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务) | 否 | 否 | 否 |
setAutoCommit:是否自动提交
commit:显示提交事务
rollback:回滚
Connection connection=DBUtil.getConnection();
connection.setAutoCommit(false);
connection.commit();
编程式事务方式需要是开发者在代码中手动的管理事务的开启、提交、回滚等操作
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;
}
});
}
声明式事务管理方法允许开发者配置的帮助下来管理事务,而不需要依赖底层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: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)
编程式事务方式需要是开发者在代码中手动的管理事务的开启、提交、回滚等操作
声明式事务管理方法允许开发者可以只使用注解或基于配置的 XML 来管理事务。
声明式事务(优):
声明式事务(缺):
参考:编程式事务和声明式事务的优缺点
-瞬时、临时状态:保存在内存的数据,程序退出就消失
-持久化:在瞬时状态和持久状态之间
-持久状态:保存在磁盘上的程序数据
-脱管、游离状态:session被关闭,对象就成为脱管的。如果再次连接到session,又会变回持久的。脱管期间的改动会保存到数据库
-对象关系映射,完成瞬态的对象数据到持久的关系型数据映射的机制
使用XML或注解用于配置和原始映射,将Java POJOs映射成数据库中的记录。运行时,占位符被指定的参数值取代,参数可能来自参数映射表、JavaBean属性、参数对象
Configuration.xml:导入依赖
<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-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(优):
Mybatis(缺):
Hibernate(优):
Hibernate(缺):
参考:MyBatis和Hibernate的优缺点对比。
Configuration的配置内容
<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
<!--当返回结果有多行时,可以使用hashmap解决类型问题-->
<!--resultType="hashmap"-->
<!--resultType:可一一对应,resultMap:无法一一对应-->
<select id="addAccount" parameterType="String">
INSERT INTO SIGNON (USERNAME,PASSWORD)
VALUES(#{username}, #{password})
</select>
-当参数类型和数据库字段类型不匹配
#{property,javaType=int,jdbcType=NUMERIC}
-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-generator:自动生成javabean和Mapper
<sqlMapGenerator targetPackage="mappers" targetProject="./src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
public void addAccount(String username,String password){
accountMapper.addAccount(username,password);
}
public List<Item> getItemListByProduct(String productId){
return itemMapper.getItemListByProduct(productId);
}
Web 开发模式
早期时代(Servlet=JSP)
MVC时代
AJAX时代(HTML:WEB服务器、CSS,JS:应用服务器)
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操作 |
资源:网络上的一个实体,或者说是网络上的一个具体信息
表现层:把"资源"具体呈现出来的形式
状态转化:如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
GET:获取资源
POST:新建资源(也可以用于更新资源)
PUT:更新资源
DELETE:删除资源。
R:网络资源/资源的表述(资源呈现出来的形式)
ST:状态转换(用户访问资源)
状态转换建立在资源表述的基础上,称为REST(思想)。符合REST设计原则的称为Restful框架。
一个资源的状态改变,应该由客户端控制。服务端只提供资源。
RESTful API是目前前后端分离的最佳实践,它是一套标准,一个规范,而不是一个框架。 如果我们在设计的时候依据RESTful准则,那么这套API这可以称作RESTful API。
特点:
RESTful API轻量,直接通过http或者https,不需要额外的协议,一般有四种常用操作post/get/put/delete。
RESTful API面向资源,一目了然,具有自解释性。
RESTful API数据的描述十分简单,基于Json或XML的数据格式进行数据传输。
每一个URI代表一种资源
参考:浅谈前后端分离及RESTful API
ex. 服务端只提供/order,如果要获取信息,用get()、新建表单用post()。接口做什么根据请求不同而变化。
(1)GET方法的查询请求应提供信息过滤功能
(2)状态码
错误信息
{
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":"..."
}
其它
@RequestParam:获取URL中的查询参数值
@RequestBody:获取POST或PUT中对象的值
@PathVariable:获取URL中的参数值
<>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
...
RESTful API请求设计规范
@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;
}
基本要求:状态码、错误码、数据封装
{
“status": 500,
"code": 50010,
"message" :"当前登入用户过多"
"datas":{
"account":
{
"username": "admin",
"email":"[email protected]",
"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:领域对象,和数据库中的表基本对应
PO:持久化对象,和数据库的表完全对应
DAO
-使用@ControllerAdvice和@ExceptionHandler
@ControllerAdvice
public class GlobalExceptionAdvice{
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ServerResponse<ExceptionResponse> handleException(Exception e){
ServerResponse<ExceptionResponse> serverResponse = new ServerResponse<>();
return serverResponse;
}
前后端未分离架构中的会话跟踪:
分离后:
JWT(Spring Security)
Header:用于描述JWT自身的元数据,包括alg(签名的算法)、typ(token的类型)
Payload:用于存放实际需要传递的数据,包括标准字段、自定义字段、官方字段
·官方字段包含iss签发人、exp过期时间、sub主题、aud使用者、nbf生效时间、iat签发时间、jti唯一编号
Signature:对上述两部分的签名(HMAC SHA256),防止数据篡改
四种授权方式:
同源策略:规范两个URL如果协议、主机名、端口均一致则同源。
ex. http://www.csu.edu.cn/ cs/index.html
跨域问题:浏览器不允许访问不同源的资源
解决:
JavaScript运行环境(解释器)
没有BOM和DOM,主要提供服务器级别的API操作(文件读写、网络服务的构建、网络通信、http服务器)
DOM 是为了操作文档出现的 API,BOM 是为了操作浏览器出现的 API
DOM 是 W3C 的标准, BOM 是 各个浏览器厂商根据 DOM在各自浏览器上的实现
参考:Javascript之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/ 来进行访问')
})
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();
模块打包程序,找到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"
}
}
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(){
}
};
(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>
);
}
}
<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:[]
}
<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>