久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

阿里大牛解析——Spring微服務(wù)專題

 新用戶0175WbuX 2022-02-09

  DI(IOC)

  何謂DI(IOC)

  DI(依賴注入)是spring的核心功能之一。

  Dependency Injection 和 Inversion of Control 其實(shí)就是一個(gè)東西的兩種不同的說(shuō)法而已,。本質(zhì)上是一回事,。Dependency Injection 是一個(gè)程序設(shè)計(jì)模式和架構(gòu)模型, 一些時(shí)候也稱作Inversion of Control,,盡管在技術(shù)上來(lái)講,,Dependency Injection 是一個(gè) Inversion of Control 的特殊實(shí)現(xiàn),Dependency Injection 是指一個(gè)對(duì)象應(yīng)用另外一個(gè)對(duì)象來(lái)提供一個(gè)特殊的能力,,例如:把一個(gè)數(shù)據(jù)庫(kù)連接以參數(shù)的形式傳到一個(gè)對(duì)象的結(jié)構(gòu)方法里面而不是在那個(gè)對(duì)象內(nèi)部自行創(chuàng)建一個(gè)連接,。Inversion of Control 和 Dependency Injection 的基本思想就是把類的依賴從類內(nèi)部轉(zhuǎn)化到外部以減少依賴。 應(yīng)用Inversion of Control,,對(duì)象在被創(chuàng)建的時(shí)候,,由一個(gè)調(diào)控系統(tǒng)內(nèi)所有對(duì)象的外界實(shí)體,將其所依賴的對(duì)象的引用,,傳遞給它,。也可以說(shuō),依賴被注入到對(duì)象中,。所以,,Inversion of Control 是,關(guān)于一個(gè)對(duì)象如何獲取他所依賴的對(duì)象的引用,,這個(gè)責(zé)任的反轉(zhuǎn),。IoC是通過(guò)處理對(duì)象定義依賴的方式來(lái)工作,也就是說(shuō),,一起協(xié)作的對(duì)象,,要么通過(guò)構(gòu)造函數(shù)參數(shù)來(lái)獲得,,要么在構(gòu)造之后給對(duì)象設(shè)置屬性來(lái)獲得,要么從工廠方法返回的方式來(lái)獲得,。容器先創(chuàng)建bean,,然后再注入這些依賴。這個(gè)獲取過(guò)程是完全反過(guò)來(lái)的,,所以命名為控制反轉(zhuǎn)(IoC),。

  DI能夠刪除任何特定的依賴于別的類或第三方接口的類,并且能夠在初始化構(gòu)造時(shí)加載要依賴的類,。DI的優(yōu)點(diǎn)是你可以依賴類的實(shí)現(xiàn)而并不需要更改你的代碼,。你甚至可以在接口不變的條件下重寫依賴的實(shí)現(xiàn)而不用改變你的編碼,即面向接口的編程,。

  文末有資料分享,,感謝讀者的閱讀。為了方便記憶,,我整理了一份腦圖,。

  阿里大牛解析——Spring微服務(wù)專題

  小編整理了一些資深架構(gòu)師們精講的資料,(包括高可用,,高并發(fā),,spring源碼,mybatis源碼,,JVM,,大數(shù)據(jù),Netty等多個(gè)技術(shù)知識(shí)的架構(gòu)視頻資料和各種電子書(shū)籍閱讀)視頻資料獲取方式幫忙 轉(zhuǎn)發(fā) 轉(zhuǎn)發(fā) 轉(zhuǎn)發(fā) 后關(guān)注我私信回復(fù)“架構(gòu)”領(lǐng)??!

  構(gòu)造器注入:通過(guò)構(gòu)造器注入,,能使當(dāng)前實(shí)例作為不可變對(duì)象,,并且能確保所有需要的依賴都是非空的.更進(jìn)一步,構(gòu)造器注入返回給客戶代碼的是一個(gè)完全初始化狀態(tài)的對(duì)象.Setter方法注入:Setter方法注入作為構(gòu)造器注入的補(bǔ)充實(shí)現(xiàn).能注入可選的有默認(rèn)值的依賴.否則,,會(huì)隨處校驗(yàn)依賴的非空與否.自動(dòng)裝配@Autowired:即通過(guò)注解自動(dòng)裝配,,默認(rèn)方式是byType.@Resource:即通過(guò)注解自動(dòng)裝配,默認(rèn)方式是[email protected]:類似與@Autowired@Qualifier:指定實(shí)現(xiàn)不同的限定符,,在具體注入時(shí),,通過(guò)該注解具體限定

  Spring容器會(huì)在容器加載時(shí)校驗(yàn)依賴非空和循環(huán)依賴.在初始化Bean時(shí),Spring會(huì)在bean真正創(chuàng)建之前盡可能晚的設(shè)置屬性和解決依賴關(guān)系.

  bean

  bean定義和依賴實(shí)現(xiàn)方式

  XML文本配置文件

  通過(guò)上面的方式來(lái)定義一個(gè)bean,,通過(guò)在bean中添加依賴來(lái)達(dá)到目的.

  主要依賴方式有:

  構(gòu)造器注入.Setter屬性方法注入

  通過(guò)XML配置文件構(gòu)造Spring beans和依賴缺失了編譯時(shí)的類型檢查,,比如構(gòu)造器參數(shù)的類型錯(cuò)誤,甚至是構(gòu)造器錯(cuò)誤的參數(shù)只有在ApplicationContext容器在運(yùn)行時(shí)構(gòu)造時(shí)才會(huì)檢查,。

  使用注解

  通過(guò)配置自動(dòng)注解掃描的根包,,并且在bean上使用注解@Component(@Service,@Repositoty,,@javax.inject.Named)等標(biāo)示他是一個(gè)bean.

  通過(guò)如上配置自動(dòng)掃描根包下的類,并作自動(dòng)注解綁定

  解釋下<

  context:annotation-config/>:

  它的作用是隱式的向Spring容器注冊(cè)

  AutowiredAnnotationBeanPostProcessor,

  CommonAnnotationBeanPostProcessor,

  PersistenceAnnotationBeanPostProcessor,

  RequiredAnnotationBeanPostProcessor

  這4個(gè)BeanPostProcessor.注冊(cè)這4個(gè)bean處理器主要的作用是為了你的系統(tǒng)能夠識(shí)別相應(yīng)的注解,。如果想使用@Autowired,@PersistenceContext,@Required,@Resource,@PostConstruct,@PreDestroy,就需要按照傳統(tǒng)聲明一條一條去聲明注解Bean,,就會(huì)顯得十分繁瑣.因此如果在Spring的配置文件中事先加上這樣一條配置的話,那么所有注解的傳統(tǒng)聲明就可以被忽略,,即不用在寫傳統(tǒng)的聲明,,Spring會(huì)自動(dòng)完成聲明。

  解釋下:

  作用是讓Bean定義注解工作起來(lái),也就是上述傳統(tǒng)聲明方式.它的base-package屬性指定了需要掃描的類包,,類包及其遞歸子包中所有的類都會(huì)被處理,。值得注意的是不但啟用了對(duì)類包進(jìn)行掃描以實(shí)施注釋驅(qū)動(dòng) Bean 定義的功能,同時(shí)還啟用了注釋驅(qū)動(dòng)自動(dòng)注入的功能(即還隱式地在內(nèi)部注冊(cè)了AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor),,因此當(dāng)使用后,,就可以將移除了。

  @Autowiredprivate UserService userService; @Service("userService")public class UserServiceImpl implements UserService

  通過(guò)如上配置定義一個(gè)bean,,并作自動(dòng)綁定

  主要注解依賴方式有:

  @Autowired:即通過(guò)注解自動(dòng)裝配,,默認(rèn)方式是byType.@Resource:即通過(guò)注解自動(dòng)裝配,默認(rèn)方式是[email protected]:類似與@Autowired

  當(dāng)通過(guò)@Autowired注入時(shí),,默認(rèn)是通過(guò)類型匹配具體的實(shí)現(xiàn)類的,,但是如果接口有多個(gè)實(shí)現(xiàn)類,Spring容器是沒(méi)法做選擇的,,有兩種方式解決這個(gè)問(wèn)題:

  @Primary注解,,指定當(dāng)有多個(gè)候選實(shí)現(xiàn)時(shí),首選這個(gè)實(shí)現(xiàn).2.@Qualifier注解指定不同實(shí)現(xiàn)不同的限定符,,在具體注入時(shí),,通過(guò)該注解具體限定.

  解釋下@Autowired:

  可以對(duì)成員變量、方法和構(gòu)造函數(shù)進(jìn)行標(biāo)注,,來(lái)完成自動(dòng)裝配的工作,。@Autowired的標(biāo)注位置不同。它們都會(huì)在Spring在初始化這個(gè)bean時(shí),,自動(dòng)裝配這個(gè)屬性,。注解之后就不需要set/get方法了。

  其中@Inject 和@Named是JSR 330 Standard Annotations.s

  通過(guò)Spring提供的擴(kuò)展方式做處理

  可以通過(guò)init-method的方式來(lái)實(shí)現(xiàn)初始化注入,,還可以通過(guò)實(shí)現(xiàn)``InitializingBean`接口來(lái)實(shí)現(xiàn),,但此種方式對(duì)業(yè)務(wù)代碼有侵入性,少用,。bean加載過(guò)程可以通過(guò)設(shè)置factory-method的方式設(shè)置工廠方法,,來(lái)設(shè)置一些靜態(tài)屬性調(diào)用getter方法,用工廠Bean PropertyPathFactoryBean調(diào)用普通方法(實(shí)例方法或者類方法),,用工廠Bean MethodInvokingFactoryBean獲取Field的值,,用工廠Bean FieldRetrievingFactoryBean

  @Configuration&@Bean

  @Bean可以出現(xiàn)在@Configurationor@Component,,其中@Configuration類似于xml中的,而@Component類似于xml中的,@Component可以作為@Configuration的替代。

  但是有一些問(wèn)題:當(dāng)我們使用@Bean注解在例如@Component作用的class里面時(shí),,將會(huì)發(fā)生一種稱之為注解@Bean的lite mode出現(xiàn),,這種不會(huì)使用CGLIB代理.所以只要我在@Bean修飾的方法之間不相互編碼調(diào)用,代碼將會(huì)很好的運(yùn)作.

  下面是@Bean的lite mode示例:

  @Component

  public class ConfigInComponent {

  @Bean

  public SimpleBean simpleBean() { return new SimpleBean();

  }

  @Bean

  public SimpleBeanConsumer simpleBeanConsumer() { return new SimpleBeanConsumer(simpleBean());

  }

  }

  上述代碼在new SimpleBeanConsumer(simpleBean())這一步實(shí)例化bean時(shí),,不會(huì)將第一步@Bean實(shí)例化的bean自動(dòng)注入到simpleBeanConsumerbean中,,而是重新用simpleBean(),生成一個(gè)新的SimpleBean 實(shí)例.而@Configuration則不會(huì)發(fā)生上述情況,代碼如下:

  @Configuration

  public class ConfigInConfiguration {

  @Bean

  public SimpleBean simpleBean() { return new SimpleBean();

  }

  @Bean

  public SimpleBeanConsumer simpleBeanConsumer() { return new SimpleBeanConsumer(simpleBean());

  }

  }

  要改善上述問(wèn)題,,可以通過(guò)以下方式實(shí)現(xiàn):

  @Component

  public class ConfigInComponent {

  @Autowired

  SimpleBean simpleBean;

  @Bean

  public SimpleBean simpleBean() { return new SimpleBean();

  }

  @Bean

  public SimpleBeanConsumer simpleBeanConsumer() { return new SimpleBeanConsumer(simpleBean);

  }

  }

  通過(guò)將@Bean生成的bean Autowired到屬性上,,并在@Bean實(shí)例化SimpleBeanConsumerbean時(shí)傳入此屬性,來(lái)達(dá)到目的.

  參考: Spring @Configuration vs @Component

  bean生命周期

  bean的作用域

  sessionrequestprototypesingletonapplication

  其中singleton是容器級(jí)別的,,即一個(gè)容器一個(gè)bean實(shí)例,spring的單例實(shí)例緩存在ConcurrentHashMap中;而GOF的單例模式是基于ClassLoader的,,即一個(gè)類加載器只能有一個(gè)實(shí)例

  通過(guò)bean后處理來(lái)增強(qiáng)功能

  BeanFactoryPostProcessor

  通過(guò)實(shí)現(xiàn)BeanFactoryPostProcessor,對(duì)bean配置的元數(shù)據(jù)做一些處理(可以改變初始化bean的內(nèi)容),,比如為安全考慮的數(shù)據(jù)庫(kù)密碼加密配置在配置文件中,,在jdbc連接數(shù)據(jù)庫(kù)時(shí)需要解密可以通過(guò)擴(kuò)展BeanFactoryPostProcessor來(lái)實(shí)現(xiàn).

  @Componentpublic class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered { @Override

  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

  BeanDefinition definition=beanFactory.getBeanDefinition("xmlBeanDefinition");

  MutablePropertyValues propertyValues=definition.getPropertyValues(); if (propertyValues.contains("name")) {

  PropertyValue property=propertyValues.getPropertyValue("name");

  String name=((TypedStringValue) property.getValue()).getValue();

  propertyValues.add("name",name.replace(" ",""));

  } if (propertyValues.contains("age")) {

  PropertyValue property=propertyValues.getPropertyValue("age");

  Double age=Double.parseDouble(((TypedStringValue) property.getValue()).getValue());

  propertyValues.add("age",Math.round(age));

  }

  } @Override

  public int getOrder() { return 3;

  }

  }

  如上,可以通過(guò)實(shí)現(xiàn)Ordered或者注解@Order的方式來(lái)指定加載順序.

  BeanPostProcessor

  通過(guò)實(shí)現(xiàn)BeanPostProcessor,,可以實(shí)現(xiàn)在Spring容器在完成Bean的實(shí)例化,,配置和其他的初始化前后做一些自己的業(yè)務(wù)處理,比如我們可以統(tǒng)計(jì)自定義的的Bean集合.

  @Componentpublic class CustomBeanPostProcessor implements BeanPostProcessor { @Override

  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(beanName.equals("userService")){

  UserService ds=(UserService) bean;

  System.out.println(ds);

  } return bean;

  } @Override

  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(beanName.equals("userService")){

  UserService ds=(UserService) bean;

  System.out.println(ds);

  } return bean;

  }

  }

  AOP(面向切面的編程)

  AOP(面向切面的編程)是通過(guò)劃分關(guān)注點(diǎn),,來(lái)做一些事,,這些事在實(shí)際編程中與整體業(yè)務(wù)無(wú)關(guān),比如事務(wù)控制,,日志管理等,通過(guò)aop將其與業(yè)務(wù)代碼解耦,,以實(shí)現(xiàn)精簡(jiǎn)的業(yè)務(wù)編碼.其整體描述為在什么時(shí)候做什么事.

  AOP三要素:

  方面(Aspect)--多個(gè)通知和切點(diǎn)的集合切入點(diǎn)(Pointcut)--在什么地方通知(增強(qiáng)處理)(Advice)--在什么時(shí)候干什么事比如有MethodInterceptor,AfterAdvice,BeforeAdvice等.描述的是在什么時(shí)候做一些增強(qiáng)處理.

  在編程配置中,如下:

  <aop:pointcut< p="">

  expression="(execution(public * com.earthlyfish.service..get*(..))) or (execution(public * com.earthlyfish.service..find*(..)))"

  id="methodCachePoint" />

  :用來(lái)定義切入點(diǎn),,該切入點(diǎn)可以重用

  :用來(lái)定義只有一個(gè)通知和一個(gè)切入點(diǎn)的切面

 ?。河脕?lái)定義切面,該切面可以包含多個(gè)切入點(diǎn)和通知,,而且標(biāo)簽內(nèi)部的通知和切入點(diǎn)定義是無(wú)序的,;和advisor的區(qū)別就在此,,advisor只包含一個(gè)通知和一個(gè)切入點(diǎn)

  事務(wù)管理

  Spring通過(guò)TransactionManager來(lái)實(shí)現(xiàn)事務(wù)管理,,現(xiàn)有兩種方式,一種是通過(guò)aop注入式的方式實(shí)現(xiàn),,另一種是通過(guò)@Transactional在方法上實(shí)現(xiàn)事務(wù)管理.

  aop注入式

  <bean id="txManager"

  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

  <tx:method name="create*" propagation="REQUIRED"

  rollback-for="java.lang.Exception,java.lang.RuntimeException" />

  <tx:method name="add*" propagation="REQUIRED"

  rollback-for="java.lang.Exception,java.lang.RuntimeException" />

  <tx:method name="save*" propagation="REQUIRED"

  rollback-for="java.lang.Exception,java.lang.RuntimeException" />

  <tx:method name="update*" propagation="REQUIRED"

  rollback-for="java.lang.Exception,java.lang.RuntimeException" />

  <tx:method name="delete*" propagation="REQUIRED"

  rollback-for="java.lang.Exception,java.lang.RuntimeException" />

  <tx:method name="remove*" propagation="REQUIRED"

  rollback-for="java.lang.Exception,java.lang.RuntimeException" />

  <aop:pointcut id="module-pointcut"

  expression="execution(* com.wyp.module.service.*.*(..))" />

  @Transactional

  需要在配置文件中有tx相關(guān)的注解.

  在需要的地方通過(guò)@Transactional注入:

  @Transactional(propagation=Propagation.REQUIRED) public UserType registerUser(Customer customer) {

  對(duì)于這種方式,,如果一個(gè)類里面有兩個(gè)方法A和B,A方法調(diào)用了B方法,,如果調(diào)用A方法的話,整個(gè)事務(wù)運(yùn)用會(huì)有下面三種情況:

  A上加了注解,,B上不加,,則整體會(huì)受事務(wù)控制.A上加了注解,B上加,,則整體會(huì)受事務(wù)控制.其實(shí)這里調(diào)用和1一樣,,在AOP代理時(shí)只給A方法加了環(huán)繞事務(wù)通知.A上不加注解,B上加,,則整體不受事務(wù)控制.這是因?yàn)檎{(diào)用A方法時(shí)由于沒(méi)有判斷到事務(wù)注解的存在,,因此代理類沒(méi)有聲稱事務(wù)控制的字節(jié)碼,這直接使用的被代理類的字節(jié)碼,,所以不受事務(wù)控制.

  編碼式

  編碼式通過(guò)在代碼中加入事務(wù)管理來(lái)達(dá)到目的,,但由于事務(wù)邏輯與主業(yè)務(wù)邏輯沒(méi)啥關(guān)系,代碼沒(méi)必要緊耦合在一起,,所以此方法不怎么使用.

  JPA知識(shí)點(diǎn)

  mapping pojo結(jié)構(gòu)詳述

  啥事不干,,先上一個(gè)多對(duì)多關(guān)系的例子.

  @Entity@Table(name="t_role")public class Role { @Id

  @GeneratedValue(strategy=GenerationType.AUTO) private Long id; @Column(name="role_name") private String roleName; @Column(name="role_description") private String roleDescription; @Column

  private boolean enabled; //角色下的所用用戶

  @JsonIgnore

  @ManyToMany(mappedBy="roles") private Set users=new HashSet<>();

  @Entity@Table(name="t_user")public class User implements Serializable, Cloneable { /**

  * 靜態(tài)long類型常量serialVersionUID的作用:

  *

  * 顯示的設(shè)置serialVersionUID值就可以保證版本的兼容性,如果你在類中寫上了這個(gè)值,,就算類變動(dòng)了,,

  * 它反序列化的時(shí)候也能和文件中的原值匹配上。而新增的值則會(huì)設(shè)置成零值,,刪除的值則不會(huì)顯示,。

  */

  private static final long serialVersionUID=-8220100956296048447L; @Id

  @GeneratedValue

  private Long id; @Column(unique=true) private String name; @Column(length=32) private String password; @Column(length=32) private String salt; @Column(precision=3) private int age; /**

  * 關(guān)系維護(hù)端,負(fù)責(zé)多對(duì)多關(guān)系的維護(hù)

  *

  * @JoinTable 表示關(guān)聯(lián)表的信息,,其中:

  * 1.name 表示關(guān)聯(lián)表的名字

  * 2.joinColumns 指定外鍵的名字,,關(guān)聯(lián)到關(guān)系維護(hù)端Role

  * 3.inverseJoinColumns 指定外鍵的名字,要關(guān)聯(lián)的關(guān)系被維護(hù)端

  * 以上完全可以默認(rèn),,默認(rèn)情況下:

  * 1.name 主表名_從表名

  * 2.joinColumns 主表_id

  * 3.inverseJoinColumns 從表_id

  */

  @ManyToMany

  @JoinTable(name="t_user_role", joinColumns=@JoinColumn(name="user_id"),inverseJoinColumns=@JoinColumn(name="role_id")) private Set roles=new HashSet<>();

  上述兩端代碼描述的是Role和User的多對(duì)多關(guān)系.

  注解說(shuō)明

  mappedBymappedBy和inverse的作用是相同的,,只不過(guò)inverse用在xml文件中,表示的意思是在關(guān)系雙方都有關(guān)系維護(hù)任務(wù)時(shí),,以哪一方為主導(dǎo),,即沒(méi)有被mappedBy修飾的一方維護(hù).即外鍵將儲(chǔ)存在沒(méi)有被mappedBy修飾一方.@Temporal@Temporal是將java.sql.*下的Data等轉(zhuǎn)成java.util.*包下的Date.@Transient@Transient標(biāo)記該字段不記錄到數(shù)據(jù)結(jié)構(gòu)

  初始化數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)

  可以通過(guò)設(shè)置hibernate.hbm2ddl.auto的值來(lái)達(dá)到初始化數(shù)據(jù)結(jié)構(gòu)的目的,有以下幾個(gè)值.

  create : 每次加載JPA,,重新創(chuàng)建數(shù)據(jù)庫(kù)表結(jié)構(gòu),,這就是導(dǎo)致數(shù)據(jù)庫(kù)表數(shù)據(jù)丟失的原因create-drop :加載JPA時(shí)創(chuàng)建,退出是刪除表結(jié)構(gòu)update :加載JPA自動(dòng)更新數(shù)據(jù)庫(kù)結(jié)構(gòu)none/validate :加載JPA時(shí),,驗(yàn)證創(chuàng)建數(shù)據(jù)庫(kù)表結(jié)構(gòu),當(dāng)然none時(shí)不做任何操作

  在本機(jī)開(kāi)發(fā)調(diào)試初始化數(shù)據(jù)的時(shí)候可以選擇create,、update等。

  但是應(yīng)用發(fā)布正式版本的時(shí)候,,對(duì)數(shù)據(jù)庫(kù)現(xiàn)有的數(shù)據(jù)或表結(jié)構(gòu)進(jìn)行自動(dòng)的更新是很危險(xiǎn)的,。此時(shí)此刻應(yīng)該由DBA同志通過(guò)手工的方式進(jìn)行后臺(tái)的數(shù)據(jù)庫(kù)操作。

  hibernate.hbm2ddl.auto的值建議是none或validate。validate應(yīng)該是最好的選擇:這樣 spring在加載之初,,如果model層和數(shù)據(jù)庫(kù)表結(jié)構(gòu)不同,,就會(huì)報(bào)錯(cuò),這樣有助于技術(shù)運(yùn)維預(yù)先發(fā)現(xiàn)問(wèn)題,。

  當(dāng)然我們可以通過(guò)自定義初始化腳本的方式來(lái)實(shí)現(xiàn)初始化數(shù)據(jù):

  通過(guò)data:

  classpath:/database/import.sql的方式來(lái)實(shí)現(xiàn)--Spring boot親測(cè)通過(guò)

  遇到的問(wèn)題

  Spring MVC轉(zhuǎn)換JPA多對(duì)多對(duì)象的json時(shí),,無(wú)限循環(huán)問(wèn)題在使用JPA處理多對(duì)多關(guān)系時(shí),發(fā)生了無(wú)限循環(huán)的問(wèn)題,,證書(shū)代碼如下:

  @Entity

  @Table(name="t_menu") public class Menu implements Comparable

{ @Id

  @GeneratedValue(strategy=GenerationType.AUTO) private long id; private String menuName; private String menuDesc; private int priority; private String staticIndex; private int parantId; private boolean enabled; @Transient

  private List

children;

  //菜單所屬role

  @ManyToMany(mappedBy="roleMenus", fetch=FetchType.LAZY) private Set roles=new HashSet<>();

  //菜單所屬role

  @ManyToMany(mappedBy="userMenus", fetch=FetchType.LAZY) private Set users=new HashSet<>();

  }

  上面轉(zhuǎn)換成json數(shù)據(jù)時(shí),,出現(xiàn)無(wú)限循環(huán)以致棧溢出.針對(duì)以上問(wèn)題可以通過(guò)以下注解實(shí)現(xiàn):使用如下:

  //菜單所屬role

  @JsonIgnore

  @ManyToMany(mappedBy="roleMenus", fetch=FetchType.LAZY) private Set roles=new HashSet<>(); //菜單所屬role

  @JsonBackReference

  @ManyToMany(mappedBy="userMenus", fetch=FetchType.LAZY) private Set users=new HashSet<>();

  @JsonIgnore json轉(zhuǎn)換時(shí)忽略某個(gè)屬性,以斷開(kāi)無(wú)限遞歸,,序列化和反序列化均忽略,,可以用在字段或get(序列化),set(反序列化)方法上@JsonBackReference json轉(zhuǎn)換時(shí)忽略某個(gè)屬性,以斷開(kāi)無(wú)限遞歸,,序列化時(shí)忽略,,可以用在字段或get(序列化),set(反序列化)方法上,序列化時(shí),相當(dāng)于@JsonIgnore@JsonManagedReference json轉(zhuǎn)換時(shí)會(huì)被序列化,,反序列化時(shí),,如果沒(méi)有該注解,則不會(huì)自動(dòng)注入@JsonBackReference標(biāo)注的屬性

  spring mvc

  Configuring a servlet container

  通過(guò)配置文件實(shí)現(xiàn)

  通過(guò)

  org.springframework.web.servlet.DispatcherServlet管理,,在web.xml里面配置:

  rest

  org.springframework.web.servlet.DispatcherServlet

  contextConfigLocation

  /WEB-INF/rest-servlet.xml

  1

  rest

  /

  如果

  contextConfigLocation

  /WEB-INF/rest-servlet.xml

  不指定,,則默認(rèn)會(huì)加載該文件WEB-INF/[servlet-name]-servlet.xml.

  通過(guò)這個(gè)web.xml,引出了另一個(gè)問(wèn)題,,即web.xml文件的加載順序是什么樣的,?

  通過(guò)查看tomcat源碼,tomcat加載web.xml的順序是:

  context-param ---> listener ---> filter ---> servlet

  首先tomcat會(huì)生成一個(gè)程序應(yīng)用級(jí)ServletContext,全局唯一,,其中將context-param放在第一位主要是listener和filter會(huì)用到配置的初始化參數(shù),,

  比如Spring配置的contextConfigLocation,在ContextLoaderLister加載時(shí)會(huì)從ServletContext的初始化參數(shù)中獲取配置文件,進(jìn)行bean的初始化操作.

  上面還有一點(diǎn),,那就是servlet的加載,,當(dāng)load-on-startup大于等于0時(shí),表示在tomcat容器啟動(dòng)時(shí)加載這個(gè)Servlet,否則,,在第一次使用時(shí)才加載.

  解釋下

   是一種簡(jiǎn)寫形式,,完全可以手動(dòng)配置替代這種簡(jiǎn)寫形式。會(huì)自動(dòng)注冊(cè)DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個(gè)bean,是spring MVC為@Controllers分發(fā)請(qǐng)求所必須的,。

  零配置實(shí)現(xiàn)

  從servlet3.0開(kāi)始,,web支持no web.xml實(shí)現(xiàn)容器的初始化工作,是得于

  javax.servlet.ServletContainerInitializer對(duì)初始化工作的支持.

  spring通過(guò)實(shí)現(xiàn)

  javax.servlet.ServletContainerInitializer,,重寫onStartUp方法,來(lái)提供無(wú)xml文件的spring容器初始化工作.下面是一個(gè)spring實(shí)現(xiàn)初始化的例子.

  import java.util.EnumSet;import javax.servlet.DispatcherType;import javax.servlet.ServletContext;import javax.servlet.ServletException;import org.springframework.web.WebApplicationInitializer;import org.springframework.web.context.ContextLoaderListener;import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;import org.springframework.web.filter.CharacterEncodingFilter;import cn.oracle.action.AppConfig;public class DefaultConfigration implements WebApplicationInitializer { @Override

  public void onStartup(ServletContext container) throws ServletException { // 配置Spring提供的字符編碼過(guò)濾器

  javax.servlet.FilterRegistration.Dynamic filter=container.addFilter("encoding", new CharacterEncodingFilter()); // 配置過(guò)濾器的過(guò)濾路徑

  filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/"); // 基于注解配置的Spring容器上下文

  AnnotationConfigWebApplicationContext rootContext=new AnnotationConfigWebApplicationContext(); // 注冊(cè)Spring容器配置類

  rootContext.register(AppConfig.class);

  container.addListener(new ContextLoaderListener(rootContext));

  }

  }

  具體的AppConfig相當(dāng)于原來(lái)的application-*.xml等spring的配置文件處理.如下:

  import org.springframework.context.annotationponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan(basePackages={"cn.oracle.*"})public class AppConfig {

  }

  可以再此基礎(chǔ)上擴(kuò)展,,編寫一個(gè)no without xml的app.

  從上可以看到WebApplicationInitializer是一個(gè)接口,和

  ServletContainerInitializer沒(méi)有關(guān)系,,下面看一下具體的細(xì)節(jié).

  下面這一段代碼是spring做處理的過(guò)程.

  @HandlesTypes({WebApplicationInitializer.class})public class SpringServletContainerInitializer

  implements ServletContainerInitializer{ public void onStartup(Set<class> webAppInitializerClasses, ServletContext servletContext)

  throws ServletException {

  List initializers=new LinkedList(); if (webAppInitializerClasses !=null) { for (Class waiClass : webAppInitializerClasses)

  { if ((!waiClass.isInterface()) && (!Modifier.isAbstract(waiClass.getModifiers())) &&

  (WebApplicationInitializer.class

  .isAssignableFrom(waiClass))) { try

  {

  initializers.add((WebApplicationInitializer)waiClass.newInstance());

  } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);

  }

  }

  }

  }

  可以看到

  SpringServletContainerInitializer實(shí)現(xiàn)了

  SpringServletContainerInitializer,而spring通過(guò)注解@HandlesTypes({

  WebApplicationInitializer.class})來(lái)實(shí)現(xiàn)掃描WebApplicationInitializer該類,,并將其注入到Set集.

  注 :@HandlesTypes is used to declare the class types that

  aServletContainerInitializer can handle.

  一些鏈接:

  基于純Java代碼的Spring容器和Web容器零配置的思考和實(shí)現(xiàn)(3) - 使用配置AOP那些事

  Spring validator

  不說(shuō)其他的,先看代碼-->

  第一步:定義一個(gè)Validator

  @Componentpublic class UserCUValidator implements Validator { private UserService userService; @Autowired

  public UserCUValidator(UserService userService) { this.userService=userService;

  } @Override

  public boolean supports(Class clazz) { return UserDomain.class.isAssignableFrom(clazz)

  || User.class.isAssignableFrom(clazz);

  } @Override

  public void validate(Object target, Errors errors) { if (target instanceof UserDomain) {

  UserDomain domain=(UserDomain) target; if (StringUtils.isNullOrEmpty(domain.getUser().getName())

  || StringUtils.isNullOrEmpty(domain.getUser().getPassword())

  || StringUtils.isNullOrEmpty(domain.getRePwd())) {

  errors.rejectValue("user.field", "user.field.invalid", "user field is invalid"); return;

  } if (!domain.getUser().getPassword().equals(domain.getRePwd())) {

  errors.rejectValue("user.password.different", "user.password.different", "user password is different"); return;

  }

  } else if (target instanceof User) {

  ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");

  ValidationUtils.rejectIfEmpty(errors, "password", "password.empty");

  }

  }

  }

  第二步:使用Validator

  @RestController@RequestMapping("/users")public class UserController { @Inject

  private UserService userService; @Autowired

  private UserCUValidator userCUValidator; /**

  * 在Spring的數(shù)據(jù)綁定器中進(jìn)行注冊(cè),,而注冊(cè)時(shí)機(jī)是特定于控制器的

  *

  * @param binder

  */

  @InitBinder

  protected void initBinder(WebDataBinder binder) {

  binder.addValidators(userCUValidator);

  } @RequestMapping(value="/login", method=RequestMethod.POST) public ResponseEntity loginUser(@RequestBody @Valid User user, HttpServletRequest request) {

  ResponseEntity responseEntity=userService.loginUser(user); if (Boolean.parseBoolean(responseEntity.getFlag())) {

  user.setPassword(null);

  HttpSession session=request.getSession();//sample as request.getSession(true)

  session.setAttribute(CommonConstant.SESSION_CURRENT_USER, user);

  } return responseEntity;

  }

  很簡(jiǎn)單,原理就是在映射到具體的方法后,,優(yōu)先validator校驗(yàn).

  關(guān)于啟動(dòng)時(shí)注解

  @SpringBootApplication@ComponentScan(basePackages="com.wyp.boot.earthlyfisher")public class BootEntry { /**

  * 1.If you need to find out what auto-configuration is currently being

  * applied, and why, start your application with the --debug switch .

  *

  * @param args

  */

  /**

  * @param args

  */

  public static void main(String[] args) { // SpringApplication.run(BootEntry.class, setBootArgs(args));

  SpringApplication application=new SpringApplication(BootEntry.class); /*

  * Banner.Mode.OFF:關(guān)閉; Banner.Mode.CONSOLE:控制臺(tái)輸出,默認(rèn)方式;

  * Banner.Mode.LOG:日志輸出方式;

  */

  application.setBannerMode(Banner.Mode.OFF);

  application.run(args); //application.run(setBootArgs(args));

  } /**

  * command line args self define

  *

  * @param args

  * @return

  */

  private static String[] setBootArgs(String[] args) {

  List argsLst=new ArrayList(); for (int i=0; i < args.length; i++) {

  argsLst.add(args[i]);

  } /** command line properties always take precedence over other property

  * sources,and self-add command param

  */

  argsLst.add("--debug");

  String[] springArgs=new String[argsLst.size()];

  argsLst.toArray(springArgs); return springArgs;

  }

  }

  @SpringBootApplication same as @Configuration @EnableAutoConfiguration @ComponentScan provides aliases to customize the attributes of @EnableAutoConfiguration and @ComponentScan

  關(guān)于日志

  如果日志配置如下:

  logging: file: D:\\image\\earthlyfihser.log path: D:\\image level: root: info

  logging.file : 輸出到指定的文件,,可以為相對(duì)路徑或者絕對(duì)路徑logging.path : 與logging.file 是互斥的,,指定文件的路徑,默認(rèn)的文件名為 spring.log

  日志文件默認(rèn)達(dá)到10Mb 時(shí),,將會(huì)從新打開(kāi)一個(gè)文件輸出,,默認(rèn)的日志輸出級(jí)別為ERROR,WARN 和 INFO,。需要注意的是日志系統(tǒng)的初始化要早于系統(tǒng)的生命周期,,因此logging properties 不能夠通過(guò)@PropertySource 注解獲取。

  這就是基礎(chǔ)的日志配置,,可以直接在applicationperties配置,,我們還可以在classpath路徑下,通過(guò)定義具體的日志文件來(lái)配置.

  Spring Boot 采用 Commons Logging 作為內(nèi)部的日志框架,。

  在默認(rèn)情況下,,采用 Starters 來(lái)啟動(dòng)Spring Boot 項(xiàng)目,Logback 是默認(rèn)的日志實(shí)現(xiàn)方案,。

  多數(shù)據(jù)源配置:

  Spring Boot多數(shù)據(jù)源配置與使用

  關(guān)于打成war

  在一般的項(xiàng)目中,,需要將項(xiàng)目打包成war包供發(fā)布到tomcat,所以自己實(shí)現(xiàn)了下:

  extends SpringBootServletInitializer,重寫configure方法.

  @SpringBootApplication

  public class BootEntry4War extends SpringBootServletInitializer { @Override

  protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(BootEntry4War.class);

  } /**

  * 1.If you need to find out what auto-configuration is currently being

  * applied, and why, start your application with the --debug switch .

  *

  * @param args

  */public static void main(String[] args) {

  SpringApplication application=new SpringApplication(BootEntry4War.class);

  application.run(args);

  }

  }

  更改pom文件打包形式為war為了確保內(nèi)嵌的servlet容器不能干擾war包將部署的servlet容器。為了達(dá)到這個(gè)目的,,你需要將內(nèi)嵌容器的依賴標(biāo)記為provided,。

  加載時(shí)預(yù)設(shè)環(huán)境變量值問(wèn)題

  關(guān)于Environment的使用

  通過(guò)實(shí)現(xiàn)EnvironmentAware可以獲取加載的環(huán)境變量,命令行參數(shù)以及在application文件預(yù)設(shè)值的變量等.

  public class BootEntry implements EnvironmentAware{ @Override

  public void setEnvironment(Environment environment) {

  System.out.println(environment.getProperty("spring.datasource.password"));

  }

  }

  當(dāng)然以上已經(jīng)是完全加載后,并初始化所有Bean后,,此時(shí)已經(jīng)不能對(duì)加載的值中間修改操作,。

  覆蓋加載的變量值的幾種方法

  通過(guò)增加EnvironmentPostProcessor的實(shí)現(xiàn)來(lái)實(shí)現(xiàn)該功能

  public class CustomeEnvPostProcessor implements EnvironmentPostProcessor {/**

  * ConfigurableEnvironment的異變PropertySource key.

  */private static final String SOURCE_NAME="applicationConfigurationProperties";/**

  * 需要的application變量的key

  */private static final String ENV_KEY_NAME="spring.datasource.password";@SuppressWarnings("unchecked")@Overridepublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {

  MutablePropertySources ms=environment.getPropertySources(); if (ms.contains(SOURCE_NAME)) { /*

  * 1.獲取加載application文件的PropertySource

  */

  PropertySource source=ms.get(SOURCE_NAME); if (source.containsProperty(ENV_KEY_NAME)) { /*

  * 2.由于復(fù)合關(guān)系,

  * 在這里找出加載application文件的PropertySource下的所有EnumerableCompositePropertySource

  */

  List lst=(List) source

  .getSource(); /*

  * 3.遍歷EnumerableCompositePropertySource集合,,

  * 找出每一個(gè)PropertySource的子PropertySource集合,并替換需要的key-value

  */

  for (EnumerableCompositePropertySource target : lst) { if (target.containsProperty(ENV_KEY_NAME)) {

  Collection<propertysource> sourceSet=target.getSource(); for (PropertySource propertySource : sourceSet) { if (propertySource instanceof MapPropertySource) {

  MapPropertySource targetSource=(MapPropertySource) propertySource;

  addOrReplaceProperty(targetSource);

  }

  }

  }

  }

  }

  }

  }/**

  * 在此做一些對(duì)application變量的替換

  *

  * @param targetSource

  */private void addOrReplaceProperty(MapPropertySource targetSource) {

  targetSource.getSource().put(ENV_KEY_NAME,

  PasswordUtil.decodePassword(targetSource.getProperty(ENV_KEY_NAME) + ""));

  }

  }

  通過(guò)追蹤源碼終于搞定了,,好辛苦.

  對(duì)于上面的實(shí)現(xiàn),需要將其加到META-INF/spring-factories文件里,如下:

  #Environment Post Processor

  org.springframework.boot.env.EnvironmentPostProcessor=\

  com.wyp.boot.earthlyfisher.system.EnvPropertiesHandler

  通過(guò)增加PropertySourceLoader的實(shí)現(xiàn)來(lái)實(shí)現(xiàn)該功能

  由于Springboot初始化加載文件是通過(guò)實(shí)現(xiàn)了PropertySourceLoader的class集來(lái)完成的,,所以可以通過(guò)這種方式來(lái)實(shí)現(xiàn).我是這么想的,,由于application文件后綴無(wú)非是yml或者properties,所以完全可以調(diào)用這兩種文件格式的加載方式來(lái)解決,需要做的只是對(duì)特定的key做擴(kuò)展.

  public class CustomPropertLoader implements PropertySourceLoader { @Override

  public String[] getFileExtensions() { return new String[] { "yml", "yaml" };

  } @Override

  public PropertySource load(String name, Resource resource, String profile) throws IOException {

  YamlPropertySourceLoader ymlLoader=new YamlPropertySourceLoader();

  PropertySource source=(MapPropertySource) ymlLoader.load(name, resource, profile);

  String propertyKey="spring.datasource.password"; if (source instanceof MapPropertySource) {

  MapPropertySource target=(MapPropertySource) source; if (target.containsProperty(propertyKey)) {

  String pwd=target.getProperty(propertyKey) + "";

  target.getSource().put(propertyKey, pwd);

  }

  } return source;

  }

  }

  當(dāng)然此種實(shí)現(xiàn),也需要將其加到META-INF/spring-factories文件里,如下:

  #PropertySourceLoaderorg.springframework.boot.envpertySourceLoader=\

  com.wyp.boot.earthlyfisher.system.CustomPropertyHandler

  重寫特定的使用實(shí)例

  比如

  spring.datasource.password是為了dataSource的使用而設(shè)置的,,完全可以自己定義數(shù)據(jù)源實(shí)例,,用來(lái)替代SpringBoot自動(dòng)配置為你生成的數(shù)據(jù)源。只不過(guò)這中方式比較復(fù)雜,得把需要的屬性重新配置一遍,,如下:

  @Configuration

  public class CustomDataSource {

  @Value(value="${spring.datasource.driverClass}")

  String driverClassName;

  @Value(value="${spring.datasource.url}")

  String url;

  @Value(value="${spring.datasource.username}")

  String username;

  @Value(value="${spring.datasource.password}")

  String password; @Bean

  @Primary

  public DataSource dataSource() {

  DataSource dataSource=new DataSource(); // org.apache.tomcat.jdbc.pool.DataSource;

  dataSource.setDriverClassName(driverClassName);

  dataSource.setUrl(url);

  dataSource.setUsername(username);

  dataSource.setPassword(password); return dataSource;

  }

  }

  覆蓋PropertyPlaceholderConfigurer的實(shí)現(xiàn)此種方式是在普通的Spring項(xiàng)目中自己處理配置文件的一般方式,,但是由于springboot內(nèi)部處理的原因,暫時(shí)還沒(méi)有成功,,具體的錯(cuò)誤原因是,,只要我覆蓋了原生的實(shí)現(xiàn),則通過(guò)自定義注解配置屬性時(shí),,會(huì)找不到,,還需看源碼以進(jìn)一步研究.錯(cuò)誤信息如下:

  Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.datasource.driver-class-name' in string value "${spring.datasource.driver-class-name}"at org.springframework.utilpertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174) ~[spring-core-4.3.4.RELEASE.jar:4.3.4.RELEASE]

  at org.springframework.utilpertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-4.3.4.RELEASE.jar:4.3.4.RELEASE]

  以上四種方式以第一種方式為佳,因?yàn)槠洳粻砍兜秸5募虞d邏輯,算是在所有參數(shù)加載完后做的一些補(bǔ)充.

  Docker化部署

  編寫Dockerfile

  FROM frolvlad/alpine-oraclejdk8:slim

  VOLUME /tmp

  ADD earthlyfisher.jar app.jar

  ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

  首先需要配置pom文件中Docker相關(guān)插件

  maven-compiler-plugin

  1.7

  1.7

  org.springframework.boot

  spring-boot-maven-plugin

  org.springframework

  springloaded

  1.2.4.RELEASE

  repackage

  com.spotify

  docker-maven-plugin

  0.4.13

  mytest/springboottest

  src/main/docker

  /

  ${project.build.directory}

  ${project.build.finalName}.jar

  earthlyfisher

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多